@@ -6,21 +6,21 @@ require ( | |||
github.com/globocom/go-redis-prometheus v0.4.0 | |||
github.com/go-redis/redis/v8 v8.11.5 | |||
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 | |||
) | |||
require ( | |||
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/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.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | |||
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/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | |||
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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | |||
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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/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.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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | |||
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/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/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.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-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-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.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-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= | |||
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.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.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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= | |||
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.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.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-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= | |||
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.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.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.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= | |||
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.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= | |||
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-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= | |||
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.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= | |||
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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= | |||
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-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.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | |||
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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= | |||
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/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= | |||
@@ -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) | |||
[![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 | |||
standard library. | |||
@@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error) | |||
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 | |||
@@ -45,19 +47,20 @@ I recommend using the latest release of Go. | |||
Here are some quick benchmarks comparing the pure-Go and assembly | |||
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 | |||
@@ -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 | |||
) | |||
// 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. | |||
type Digest struct { | |||
@@ -50,10 +42,10 @@ func New() *Digest { | |||
// Reset clears the Digest's state so that it can be reused. | |||
func (d *Digest) Reset() { | |||
d.v1 = prime1v + prime2 | |||
d.v1 = primes[0] + prime2 | |||
d.v2 = prime2 | |||
d.v3 = 0 | |||
d.v4 = -prime1v | |||
d.v4 = -primes[0] | |||
d.total = 0 | |||
d.n = 0 | |||
} | |||
@@ -69,21 +61,23 @@ func (d *Digest) Write(b []byte) (n int, err error) { | |||
n = len(b) | |||
d.total += uint64(n) | |||
memleft := d.mem[d.n&(len(d.mem)-1):] | |||
if d.n+n < 32 { | |||
// This new data doesn't even fill the current block. | |||
copy(d.mem[d.n:], b) | |||
copy(memleft, b) | |||
d.n += n | |||
return | |||
} | |||
if d.n > 0 { | |||
// 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.v2 = round(d.v2, u64(d.mem[8:16])) | |||
d.v3 = round(d.v3, u64(d.mem[16:24])) | |||
d.v4 = round(d.v4, u64(d.mem[24:32])) | |||
b = b[32-d.n:] | |||
b = b[c:] | |||
d.n = 0 | |||
} | |||
@@ -133,21 +127,20 @@ func (d *Digest) Sum64() uint64 { | |||
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 = 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 | |||
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 | |||
i++ | |||
} | |||
h ^= h >> 33 | |||
@@ -1,215 +1,209 @@ | |||
//go:build !appengine && gc && !purego | |||
// +build !appengine | |||
// +build gc | |||
// +build !purego | |||
#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 | |||
TEXT ·Sum64(SB), NOSPLIT, $0-32 | |||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 | |||
// 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. | |||
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. | |||
SUBQ $32, BX | |||
SUBQ $32, end | |||
// Check whether we have at least one block. | |||
CMPQ DX, $32 | |||
CMPQ n, $32 | |||
JLT noBlocks | |||
// 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 | |||
noBlocks: | |||
MOVQ ·prime5v(SB), AX | |||
MOVQ ·primes+32(SB), h | |||
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 | |||
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: | |||
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 | |||
// writeBlocks uses the same registers as above except that it uses AX to store | |||
// the d pointer. | |||
// 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. | |||
MOVQ ·prime1v(SB), R13 | |||
MOVQ ·prime2v(SB), R14 | |||
MOVQ ·primes+0(SB), prime1 | |||
MOVQ ·primes+8(SB), prime2 | |||
// 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. | |||
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 | |||
// 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. | |||
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 |
@@ -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 gc | |||
// +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 | |||
@@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 { | |||
var h uint64 | |||
if n >= 32 { | |||
v1 := prime1v + prime2 | |||
v1 := primes[0] + prime2 | |||
v2 := prime2 | |||
v3 := uint64(0) | |||
v4 := -prime1v | |||
v4 := -primes[0] | |||
for len(b) >= 32 { | |||
v1 = round(v1, u64(b[0:8:len(b)])) | |||
v2 = round(v2, u64(b[8:16:len(b)])) | |||
@@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 { | |||
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 = 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 | |||
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 | |||
} | |||
@@ -1,3 +1,4 @@ | |||
//go:build appengine | |||
// +build appengine | |||
// This file contains the safe implementations of otherwise unsafe-using code. | |||
@@ -1,3 +1,4 @@ | |||
//go:build !appengine | |||
// +build !appengine | |||
// This file encapsulates usage of unsafe. | |||
@@ -11,7 +12,7 @@ import ( | |||
// In the future it's possible that compiler optimizations will make these | |||
// 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 | |||
// 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. | |||
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. | |||
// | |||
// 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 { | |||
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 | |||
if e := c.exemplar.Load(); e != nil { | |||
exemplar = e.(*dto.Exemplar) | |||
} | |||
val := c.get() | |||
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 | |||
// partitioned by the given label names. | |||
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), | |||
opts.Help, | |||
labelNames, | |||
opts.VariableLabels, | |||
opts.ConstLabels, | |||
) | |||
return &CounterVec{ | |||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | |||
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.init(result) // Init self-collection. | |||
@@ -245,7 +266,8 @@ func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { | |||
// WithLabelValues works as GetMetricWithLabelValues, but panics where | |||
// GetMetricWithLabelValues would have returned an error. Not returning an | |||
// error allows shortcuts like | |||
// myVec.WithLabelValues("404", "GET").Add(42) | |||
// | |||
// myVec.WithLabelValues("404", "GET").Add(42) | |||
func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | |||
c, err := v.GetMetricWithLabelValues(lvs...) | |||
if err != nil { | |||
@@ -256,7 +278,8 @@ func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | |||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | |||
// 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 { | |||
c, err := v.GetMetricWith(labels) | |||
if err != nil { | |||
@@ -14,20 +14,16 @@ | |||
package prometheus | |||
import ( | |||
"errors" | |||
"fmt" | |||
"sort" | |||
"strings" | |||
"github.com/cespare/xxhash/v2" | |||
"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" | |||
"github.com/prometheus/common/model" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
// 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 | |||
// the constant labels. | |||
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 | |||
// must be unique among all registered descriptors and can therefore be | |||
// 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 | |||
// specified in the Desc. See the Collector example for a usage pattern. | |||
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{ | |||
fqName: fqName, | |||
help: help, | |||
variableLabels: variableLabels, | |||
variableLabels: variableLabels.constrainedLabels(), | |||
} | |||
if !model.IsValidMetricName(model.LabelValue(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). | |||
labelValues := make([]string, 1, len(constLabels)+1) | |||
labelValues[0] = fqName | |||
labelNames := make([]string, 0, len(constLabels)+len(variableLabels)) | |||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels)) | |||
labelNameSet := map[string]struct{}{} | |||
// First add only the const label names and sort them... | |||
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 | |||
// cannot be in a regular label name. That prevents matching the label | |||
// 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 | |||
} | |||
labelNames = append(labelNames, "$"+labelName) | |||
labelNameSet[labelName] = struct{}{} | |||
labelNames = append(labelNames, "$"+label.Name) | |||
labelNameSet[label.Name] = struct{}{} | |||
} | |||
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 | |||
} | |||
@@ -21,55 +21,66 @@ | |||
// All exported functions and methods are safe to be used concurrently unless | |||
// specified otherwise. | |||
// | |||
// A Basic Example | |||
// # A Basic 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, | |||
// 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 | |||
// 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 | |||
// …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 | |||
// 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 | |||
// shortcuts. | |||
// | |||
// Advanced Uses of the Registry | |||
// # Advanced Uses of the Registry | |||
// | |||
// 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. | |||
@@ -176,23 +187,23 @@ | |||
// NewProcessCollector). With a custom registry, you are in control and decide | |||
// yourself about the Collectors to register. | |||
// | |||
// HTTP Exposition | |||
// # HTTP Exposition | |||
// | |||
// The Registry implements the Gatherer interface. The caller of the Gather | |||
// 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 | |||
// 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. | |||
// | |||
// Graphite Bridge | |||
// # Graphite Bridge | |||
// | |||
// Functions and examples to push metrics from a Gatherer to Graphite can be | |||
// 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 | |||
// of the existing implementations. | |||
@@ -55,6 +55,18 @@ type Gauge interface { | |||
// GaugeOpts is an alias for Opts. See there for doc comments. | |||
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. | |||
// | |||
// 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 | |||
// partitioned by the given label names. | |||
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), | |||
opts.Help, | |||
labelNames, | |||
opts.VariableLabels, | |||
opts.ConstLabels, | |||
) | |||
return &GaugeVec{ | |||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { | |||
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.init(result) // Init self-collection. | |||
@@ -210,7 +230,8 @@ func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { | |||
// WithLabelValues works as GetMetricWithLabelValues, but panics where | |||
// GetMetricWithLabelValues would have returned an error. Not returning an | |||
// error allows shortcuts like | |||
// myVec.WithLabelValues("404", "GET").Add(42) | |||
// | |||
// myVec.WithLabelValues("404", "GET").Add(42) | |||
func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | |||
g, err := v.GetMetricWithLabelValues(lvs...) | |||
if err != nil { | |||
@@ -221,7 +242,8 @@ func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | |||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | |||
// 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 { | |||
g, err := v.GetMetricWith(labels) | |||
if err != nil { | |||
@@ -23,11 +23,10 @@ import ( | |||
"strings" | |||
"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" | |||
dto "github.com/prometheus/client_model/go" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
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: | |||
// | |||
// 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, | |||
// 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 | |||
// 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 | |||
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of | |||
// 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 | |||
// create a Desc. | |||
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 | |||
// label names. | |||
const reservedLabelPrefix = "__" | |||
@@ -20,11 +20,9 @@ import ( | |||
"strings" | |||
"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" | |||
"github.com/prometheus/common/model" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with xxhash. | |||
@@ -187,7 +185,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { | |||
} 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. | |||
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)), | |||
Exemplar: e, | |||
} | |||
@@ -68,17 +68,17 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou | |||
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) { | |||
resp, err := next.RoundTrip(r) | |||
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 | |||
} | |||
@@ -111,17 +111,18 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT | |||
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) { | |||
start := time.Now() | |||
resp, err := next.RoundTrip(r) | |||
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 | |||
} | |||
@@ -28,7 +28,9 @@ import ( | |||
// magicString is used for the hacky label test in checkLabels. Remove once fixed. | |||
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 { | |||
obs.Observe(val) | |||
return | |||
@@ -36,7 +38,9 @@ func exemplarObserve(obs prometheus.Observer, val float64, labels map[string]str | |||
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 { | |||
obs.Add(val) | |||
return | |||
@@ -83,7 +87,8 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op | |||
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 { | |||
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) | |||
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) { | |||
now := time.Now() | |||
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) | |||
} | |||
code, method := checkLabels(counter) | |||
// Curry the counter with dynamic labels before checking the remaining labels. | |||
code, method := checkLabels(counter.MustCurryWith(hOpts.emptyDynamicLabels())) | |||
if code { | |||
return func(w http.ResponseWriter, r *http.Request) { | |||
d := newDelegator(w, nil) | |||
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) { | |||
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) | |||
} | |||
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) { | |||
now := time.Now() | |||
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) | |||
} | |||
@@ -227,28 +234,32 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, | |||
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 { | |||
return func(w http.ResponseWriter, r *http.Request) { | |||
d := newDelegator(w, nil) | |||
next.ServeHTTP(d, 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) { | |||
next.ServeHTTP(w, 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) | |||
} | |||
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) { | |||
d := newDelegator(w, nil) | |||
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) | |||
} | |||
// 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. | |||
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 { | |||
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) | |||
@@ -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 { | |||
return optionApplyFunc(func(o *options) { | |||
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" | |||
"runtime" | |||
"sort" | |||
"strconv" | |||
"strings" | |||
"sync" | |||
"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" | |||
"github.com/prometheus/client_golang/prometheus/internal" | |||
"github.com/prometheus/common/expfmt" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
const ( | |||
@@ -252,9 +251,12 @@ func (errs MultiError) MaybeUnwrap() error { | |||
} | |||
// 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 { | |||
mtx sync.RWMutex | |||
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() | |||
} | |||
// 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 | |||
// Prometheus text format, and writes it to a temporary file. Upon success, the | |||
// temporary file is renamed to the provided filename. | |||
@@ -905,6 +932,10 @@ func checkMetricConsistency( | |||
h.WriteString(lp.GetValue()) | |||
h.Write(separatorByteSlice) | |||
} | |||
if dtoMetric.TimestampMs != nil { | |||
h.WriteString(strconv.FormatInt(*(dtoMetric.TimestampMs), 10)) | |||
h.Write(separatorByteSlice) | |||
} | |||
hSum := h.Sum64() | |||
if _, exists := metricHashes[hSum]; exists { | |||
return fmt.Errorf( | |||
@@ -934,7 +965,7 @@ func checkDescConsistency( | |||
copy(lpsFromDesc, desc.constLabelPairs) | |||
for _, l := range desc.variableLabels { | |||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ | |||
Name: proto.String(l), | |||
Name: proto.String(l.Name), | |||
}) | |||
} | |||
if len(lpsFromDesc) != len(dtoMetric.Label) { | |||
@@ -22,11 +22,10 @@ import ( | |||
"sync/atomic" | |||
"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" | |||
"github.com/beorn7/perks/quantile" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
// quantileLabel is used for the label that defines the quantile in a | |||
@@ -148,6 +147,18 @@ type SummaryOpts struct { | |||
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 | |||
// perk/quantile is actually not working as advertised - and it might be | |||
// 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 { | |||
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 { | |||
if n == quantileLabel { | |||
if n.Name == quantileLabel { | |||
panic(errQuantileLabelNotAllowed) | |||
} | |||
} | |||
@@ -530,20 +541,28 @@ type SummaryVec struct { | |||
// it is handled by the Prometheus server internally, “quantile” is an illegal | |||
// label name. NewSummaryVec will panic if this label name is used. | |||
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 { | |||
panic(errQuantileLabelNotAllowed) | |||
} | |||
} | |||
desc := NewDesc( | |||
desc := V2.NewDesc( | |||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | |||
opts.Help, | |||
labelNames, | |||
opts.VariableLabels, | |||
opts.ConstLabels, | |||
) | |||
return &SummaryVec{ | |||
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 | |||
// GetMetricWithLabelValues would have returned an error. Not returning an | |||
// 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 { | |||
s, err := v.GetMetricWithLabelValues(lvs...) | |||
if err != nil { | |||
@@ -614,7 +634,8 @@ func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { | |||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have | |||
// 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 { | |||
s, err := v.GetMetricWith(labels) | |||
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 | |||
// 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 | |||
// 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 | |||
// 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: | |||
// 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 { | |||
return &Timer{ | |||
begin: time.Now(), | |||
@@ -52,3 +63,19 @@ func (t *Timer) ObserveDuration() time.Duration { | |||
} | |||
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" | |||
"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" | |||
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. | |||
@@ -188,9 +186,9 @@ func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair { | |||
return desc.constLabelPairs | |||
} | |||
labelPairs := make([]*dto.LabelPair, 0, totalLen) | |||
for i, n := range desc.variableLabels { | |||
for i, l := range desc.variableLabels { | |||
labelPairs = append(labelPairs, &dto.LabelPair{ | |||
Name: proto.String(n), | |||
Name: proto.String(l.Name), | |||
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). | |||
// See also the CounterVec example. | |||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { | |||
lvs = constrainLabelValues(m.desc, lvs, m.curry) | |||
h, err := m.hashLabelValues(lvs) | |||
if err != nil { | |||
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 | |||
// there for pros and cons of the two methods. | |||
func (m *MetricVec) Delete(labels Labels) bool { | |||
labels = constrainLabels(m.desc, labels) | |||
h, err := m.hashLabels(labels) | |||
if err != nil { | |||
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. | |||
// To match curried labels with DeletePartialMatch, it must be called on the base vector. | |||
func (m *MetricVec) DeletePartialMatch(labels Labels) int { | |||
labels = constrainLabels(m.desc, labels) | |||
return m.metricMap.deleteByLabels(labels, m.curry) | |||
} | |||
@@ -145,10 +148,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||
iCurry int | |||
) | |||
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 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]) | |||
iCurry++ | |||
@@ -156,7 +159,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||
if !ok { | |||
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 { | |||
@@ -199,6 +202,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { | |||
// a wrapper around MetricVec, implementing a vector for a specific Metric | |||
// implementation, for example GaugeVec. | |||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { | |||
lvs = constrainLabelValues(m.desc, lvs, m.curry) | |||
h, err := m.hashLabelValues(lvs) | |||
if err != nil { | |||
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, | |||
// for example GaugeVec. | |||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { | |||
labels = constrainLabels(m.desc, labels) | |||
h, err := m.hashLabels(labels) | |||
if err != nil { | |||
return nil, err | |||
@@ -266,16 +271,16 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { | |||
iCurry int | |||
) | |||
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 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) | |||
iCurry++ | |||
} else { | |||
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) | |||
} | |||
@@ -453,7 +458,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values [] | |||
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { | |||
for l, v := range labels { | |||
// 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 { | |||
// Check the value of that label against the target value. | |||
// We don't consider curried values in partial matches. | |||
@@ -605,7 +610,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe | |||
iCurry++ | |||
continue | |||
} | |||
if values[i] != labels[k] { | |||
if values[i] != labels[k.Name] { | |||
return false | |||
} | |||
} | |||
@@ -621,7 +626,7 @@ func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) [] | |||
iCurry++ | |||
continue | |||
} | |||
labelValues[i] = labels[k] | |||
labelValues[i] = labels[k.Name] | |||
} | |||
return labelValues | |||
} | |||
@@ -640,3 +645,34 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { | |||
} | |||
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" | |||
"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" | |||
"github.com/prometheus/client_golang/prometheus/internal" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
// WrapRegistererWith returns a Registerer wrapping the provided | |||
@@ -206,7 +204,7 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { | |||
constLabels[ln] = lv | |||
} | |||
// 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 | |||
// created by NewDesc above, i.e. earlier errors get precedence. | |||
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. | |||
type textDecoder struct { | |||
r io.Reader | |||
p TextParser | |||
fams []*dto.MetricFamily | |||
fams map[string]*dto.MetricFamily | |||
err error | |||
} | |||
// Decode implements the Decoder interface. | |||
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 | |||
@@ -18,9 +18,9 @@ import ( | |||
"io" | |||
"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/prometheus/common/internal/bitbucket.org/ww/goautoneg" | |||
"google.golang.org/protobuf/encoding/prototext" | |||
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 == "") { | |||
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 | |||
@@ -133,7 +136,7 @@ func NewEncoder(w io.Writer, format Format) Encoder { | |||
case FmtProtoText: | |||
return encoderCloser{ | |||
encode: func(v *dto.MetricFamily) error { | |||
_, err := fmt.Fprintln(w, proto.MarshalTextString(v)) | |||
_, err := fmt.Fprintln(w, prototext.Format(v)) | |||
return err | |||
}, | |||
close: func() error { return nil }, | |||
@@ -146,7 +149,7 @@ func NewEncoder(w io.Writer, format Format) Encoder { | |||
}, | |||
close: func() error { return nil }, | |||
} | |||
case FmtOpenMetrics: | |||
case FmtOpenMetrics_0_0_1, FmtOpenMetrics_1_0_0: | |||
return encoderCloser{ | |||
encode: func(v *dto.MetricFamily) error { | |||
_, err := MetricFamilyToOpenMetrics(w, v) | |||
@@ -19,20 +19,22 @@ type Format string | |||
// Constants to assemble the Content-Type values for the different wire protocols. | |||
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. | |||
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 ( | |||
@@ -21,8 +21,8 @@ import "bytes" | |||
// 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. | |||
func Fuzz(in []byte) int { | |||
@@ -46,20 +46,20 @@ import ( | |||
// missing features and peculiarities to avoid complications when switching from | |||
// 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) { | |||
name := in.GetName() | |||
if name == "" { | |||
@@ -17,7 +17,6 @@ import ( | |||
"bufio" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"math" | |||
"strconv" | |||
"strings" | |||
@@ -44,7 +43,7 @@ const ( | |||
var ( | |||
bufPool = sync.Pool{ | |||
New: func() interface{} { | |||
return bufio.NewWriter(ioutil.Discard) | |||
return bufio.NewWriter(io.Discard) | |||
}, | |||
} | |||
numBufPool = sync.Pool{ | |||
@@ -24,8 +24,8 @@ import ( | |||
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" | |||
"google.golang.org/protobuf/proto" | |||
) | |||
// 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 { | |||
p.lineCount++ | |||
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 | |||
} | |||
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 | |||
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 | |||
"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 | |||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
package goautoneg | |||
@@ -18,7 +18,6 @@ import ( | |||
"errors" | |||
"fmt" | |||
"math" | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
"time" | |||
@@ -183,54 +182,78 @@ func (d *Duration) Type() string { | |||
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 | |||
// 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": | |||
// Allow 0 without a unit. | |||
return 0, nil | |||
case "": | |||
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). | |||
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 { | |||
@@ -16,20 +16,12 @@ package model | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"math" | |||
"sort" | |||
"strconv" | |||
"strings" | |||
) | |||
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 | |||
// 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 | |||
@@ -38,82 +30,14 @@ var ( | |||
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 { | |||
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 | |||
@@ -129,11 +53,19 @@ func (s *Sample) Equal(o *Sample) bool { | |||
if !s.Timestamp.Equal(o.Timestamp) { | |||
return false | |||
} | |||
if s.Histogram != nil { | |||
return s.Histogram.Equal(o.Histogram) | |||
} | |||
return s.Value.Equal(o.Value) | |||
} | |||
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{ | |||
Timestamp: s.Timestamp, | |||
Value: s.Value, | |||
@@ -142,6 +74,19 @@ func (s Sample) String() string { | |||
// MarshalJSON implements json.Marshaler. | |||
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 { | |||
Metric Metric `json:"metric"` | |||
Value SamplePair `json:"value"` | |||
@@ -152,21 +97,25 @@ func (s Sample) MarshalJSON() ([]byte, error) { | |||
Value: s.Value, | |||
}, | |||
} | |||
return json.Marshal(&v) | |||
} | |||
// UnmarshalJSON implements json.Unmarshaler. | |||
func (s *Sample) UnmarshalJSON(b []byte) error { | |||
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, | |||
Value: SamplePair{ | |||
Timestamp: s.Timestamp, | |||
Value: s.Value, | |||
}, | |||
Histogram: SampleHistogramPair{ | |||
Timestamp: s.Timestamp, | |||
Histogram: s.Histogram, | |||
}, | |||
} | |||
if err := json.Unmarshal(b, &v); err != nil { | |||
@@ -174,8 +123,13 @@ func (s *Sample) UnmarshalJSON(b []byte) error { | |||
} | |||
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 | |||
} | |||
@@ -221,80 +175,76 @@ func (s Samples) Equal(o Samples) bool { | |||
// SampleStream is a stream of Values belonging to an attached COWMetric. | |||
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 { | |||
vals := make([]string, len(ss.Values)) | |||
valuesLength := len(ss.Values) | |||
vals := make([]string, valuesLength+len(ss.Histograms)) | |||
for i, v := range ss.Values { | |||
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")) | |||
} | |||
// 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 | |||
} | |||
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. | |||
@@ -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 | |||
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 | |||
SKIP_GOLANGCI_LINT := | |||
GOLANGCI_LINT := | |||
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. | |||
# windows isn't included here because of the path separator being different. | |||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) | |||
ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) | |||
# 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. | |||
ifeq (,$(CIRCLE_JOB)) | |||
ifneq (,$(SKIP_GOLANGCI_LINT)) | |||
GOLANGCI_LINT := | |||
else ifeq (,$(CIRCLE_JOB)) | |||
GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | |||
else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) | |||
GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint | |||
@@ -380,6 +380,42 @@ func parseCPUInfoMips(info []byte) ([]CPUInfo, error) { | |||
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) { | |||
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 | |||
// 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 | |||
@@ -16,30 +16,29 @@ | |||
// | |||
// 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 |
@@ -284,7 +284,8 @@ func parseMountStats(r io.Reader) ([]*Mount, error) { | |||
} | |||
// 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) { | |||
if len(ss) < deviceEntryLen { | |||
return nil, fmt.Errorf("invalid device entry: %v", ss) | |||
@@ -27,8 +27,9 @@ import ( | |||
// For the proc file format details, | |||
// See: | |||
// * 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. | |||
type SoftnetStat struct { | |||
@@ -38,6 +39,18 @@ type SoftnetStat struct { | |||
Dropped uint32 | |||
// Number of times processing packets ran out of quota. | |||
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" | |||
@@ -66,22 +79,57 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) { | |||
for s.Scan() { | |||
columns := strings.Fields(s.Text()) | |||
width := len(columns) | |||
softnetStat := SoftnetStat{} | |||
if 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 | |||
@@ -15,6 +15,7 @@ package procfs | |||
import ( | |||
"bufio" | |||
"io" | |||
"os" | |||
"path/filepath" | |||
"strconv" | |||
@@ -42,27 +43,43 @@ func (fs FS) NetStat() ([]NetStat, error) { | |||
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" | |||
) | |||
// 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 | |||
// 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 | |||
@@ -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 | |||
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 | |||
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) { | |||
@@ -174,14 +175,14 @@ func (p Proc) Netstat() (ProcNetstat, error) { | |||
if err != nil { | |||
return ProcNetstat{PID: p.PID}, err | |||
} | |||
procNetstat, err := parseNetstat(bytes.NewReader(data), filename) | |||
procNetstat, err := parseProcNetstat(bytes.NewReader(data), filename) | |||
procNetstat.PID = p.PID | |||
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. | |||
func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||
func parseProcNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||
var ( | |||
scanner = bufio.NewScanner(r) | |||
procNetstat = ProcNetstat{} | |||
@@ -208,230 +209,232 @@ func parseNetstat(r io.Reader, fileName string) (ProcNetstat, error) { | |||
case "TcpExt": | |||
switch key { | |||
case "SyncookiesSent": | |||
procNetstat.TcpExt.SyncookiesSent = value | |||
procNetstat.TcpExt.SyncookiesSent = &value | |||
case "SyncookiesRecv": | |||
procNetstat.TcpExt.SyncookiesRecv = value | |||
procNetstat.TcpExt.SyncookiesRecv = &value | |||
case "SyncookiesFailed": | |||
procNetstat.TcpExt.SyncookiesFailed = value | |||
procNetstat.TcpExt.SyncookiesFailed = &value | |||
case "EmbryonicRsts": | |||
procNetstat.TcpExt.EmbryonicRsts = value | |||
procNetstat.TcpExt.EmbryonicRsts = &value | |||
case "PruneCalled": | |||
procNetstat.TcpExt.PruneCalled = value | |||
procNetstat.TcpExt.PruneCalled = &value | |||
case "RcvPruned": | |||
procNetstat.TcpExt.RcvPruned = value | |||
procNetstat.TcpExt.RcvPruned = &value | |||
case "OfoPruned": | |||
procNetstat.TcpExt.OfoPruned = value | |||
procNetstat.TcpExt.OfoPruned = &value | |||
case "OutOfWindowIcmps": | |||
procNetstat.TcpExt.OutOfWindowIcmps = value | |||
procNetstat.TcpExt.OutOfWindowIcmps = &value | |||
case "LockDroppedIcmps": | |||
procNetstat.TcpExt.LockDroppedIcmps = value | |||
procNetstat.TcpExt.LockDroppedIcmps = &value | |||
case "ArpFilter": | |||
procNetstat.TcpExt.ArpFilter = value | |||
procNetstat.TcpExt.ArpFilter = &value | |||
case "TW": | |||
procNetstat.TcpExt.TW = value | |||
procNetstat.TcpExt.TW = &value | |||
case "TWRecycled": | |||
procNetstat.TcpExt.TWRecycled = value | |||
procNetstat.TcpExt.TWRecycled = &value | |||
case "TWKilled": | |||
procNetstat.TcpExt.TWKilled = value | |||
procNetstat.TcpExt.TWKilled = &value | |||
case "PAWSActive": | |||
procNetstat.TcpExt.PAWSActive = value | |||
procNetstat.TcpExt.PAWSActive = &value | |||
case "PAWSEstab": | |||
procNetstat.TcpExt.PAWSEstab = value | |||
procNetstat.TcpExt.PAWSEstab = &value | |||
case "DelayedACKs": | |||
procNetstat.TcpExt.DelayedACKs = value | |||
procNetstat.TcpExt.DelayedACKs = &value | |||
case "DelayedACKLocked": | |||
procNetstat.TcpExt.DelayedACKLocked = value | |||
procNetstat.TcpExt.DelayedACKLocked = &value | |||
case "DelayedACKLost": | |||
procNetstat.TcpExt.DelayedACKLost = value | |||
procNetstat.TcpExt.DelayedACKLost = &value | |||
case "ListenOverflows": | |||
procNetstat.TcpExt.ListenOverflows = value | |||
procNetstat.TcpExt.ListenOverflows = &value | |||
case "ListenDrops": | |||
procNetstat.TcpExt.ListenDrops = value | |||
procNetstat.TcpExt.ListenDrops = &value | |||
case "TCPHPHits": | |||
procNetstat.TcpExt.TCPHPHits = value | |||
procNetstat.TcpExt.TCPHPHits = &value | |||
case "TCPPureAcks": | |||
procNetstat.TcpExt.TCPPureAcks = value | |||
procNetstat.TcpExt.TCPPureAcks = &value | |||
case "TCPHPAcks": | |||
procNetstat.TcpExt.TCPHPAcks = value | |||
procNetstat.TcpExt.TCPHPAcks = &value | |||
case "TCPRenoRecovery": | |||
procNetstat.TcpExt.TCPRenoRecovery = value | |||
procNetstat.TcpExt.TCPRenoRecovery = &value | |||
case "TCPSackRecovery": | |||
procNetstat.TcpExt.TCPSackRecovery = value | |||
procNetstat.TcpExt.TCPSackRecovery = &value | |||
case "TCPSACKReneging": | |||
procNetstat.TcpExt.TCPSACKReneging = value | |||
procNetstat.TcpExt.TCPSACKReneging = &value | |||
case "TCPSACKReorder": | |||
procNetstat.TcpExt.TCPSACKReorder = value | |||
procNetstat.TcpExt.TCPSACKReorder = &value | |||
case "TCPRenoReorder": | |||
procNetstat.TcpExt.TCPRenoReorder = value | |||
procNetstat.TcpExt.TCPRenoReorder = &value | |||
case "TCPTSReorder": | |||
procNetstat.TcpExt.TCPTSReorder = value | |||
procNetstat.TcpExt.TCPTSReorder = &value | |||
case "TCPFullUndo": | |||
procNetstat.TcpExt.TCPFullUndo = value | |||
procNetstat.TcpExt.TCPFullUndo = &value | |||
case "TCPPartialUndo": | |||
procNetstat.TcpExt.TCPPartialUndo = value | |||
procNetstat.TcpExt.TCPPartialUndo = &value | |||
case "TCPDSACKUndo": | |||
procNetstat.TcpExt.TCPDSACKUndo = value | |||
procNetstat.TcpExt.TCPDSACKUndo = &value | |||
case "TCPLossUndo": | |||
procNetstat.TcpExt.TCPLossUndo = value | |||
procNetstat.TcpExt.TCPLossUndo = &value | |||
case "TCPLostRetransmit": | |||
procNetstat.TcpExt.TCPLostRetransmit = value | |||
procNetstat.TcpExt.TCPLostRetransmit = &value | |||
case "TCPRenoFailures": | |||
procNetstat.TcpExt.TCPRenoFailures = value | |||
procNetstat.TcpExt.TCPRenoFailures = &value | |||
case "TCPSackFailures": | |||
procNetstat.TcpExt.TCPSackFailures = value | |||
procNetstat.TcpExt.TCPSackFailures = &value | |||
case "TCPLossFailures": | |||
procNetstat.TcpExt.TCPLossFailures = value | |||
procNetstat.TcpExt.TCPLossFailures = &value | |||
case "TCPFastRetrans": | |||
procNetstat.TcpExt.TCPFastRetrans = value | |||
procNetstat.TcpExt.TCPFastRetrans = &value | |||
case "TCPSlowStartRetrans": | |||
procNetstat.TcpExt.TCPSlowStartRetrans = value | |||
procNetstat.TcpExt.TCPSlowStartRetrans = &value | |||
case "TCPTimeouts": | |||
procNetstat.TcpExt.TCPTimeouts = value | |||
procNetstat.TcpExt.TCPTimeouts = &value | |||
case "TCPLossProbes": | |||
procNetstat.TcpExt.TCPLossProbes = value | |||
procNetstat.TcpExt.TCPLossProbes = &value | |||
case "TCPLossProbeRecovery": | |||
procNetstat.TcpExt.TCPLossProbeRecovery = value | |||
procNetstat.TcpExt.TCPLossProbeRecovery = &value | |||
case "TCPRenoRecoveryFail": | |||
procNetstat.TcpExt.TCPRenoRecoveryFail = value | |||
procNetstat.TcpExt.TCPRenoRecoveryFail = &value | |||
case "TCPSackRecoveryFail": | |||
procNetstat.TcpExt.TCPSackRecoveryFail = value | |||
procNetstat.TcpExt.TCPSackRecoveryFail = &value | |||
case "TCPRcvCollapsed": | |||
procNetstat.TcpExt.TCPRcvCollapsed = value | |||
procNetstat.TcpExt.TCPRcvCollapsed = &value | |||
case "TCPDSACKOldSent": | |||
procNetstat.TcpExt.TCPDSACKOldSent = value | |||
procNetstat.TcpExt.TCPDSACKOldSent = &value | |||
case "TCPDSACKOfoSent": | |||
procNetstat.TcpExt.TCPDSACKOfoSent = value | |||
procNetstat.TcpExt.TCPDSACKOfoSent = &value | |||
case "TCPDSACKRecv": | |||
procNetstat.TcpExt.TCPDSACKRecv = value | |||
procNetstat.TcpExt.TCPDSACKRecv = &value | |||
case "TCPDSACKOfoRecv": | |||
procNetstat.TcpExt.TCPDSACKOfoRecv = value | |||
procNetstat.TcpExt.TCPDSACKOfoRecv = &value | |||
case "TCPAbortOnData": | |||
procNetstat.TcpExt.TCPAbortOnData = value | |||
procNetstat.TcpExt.TCPAbortOnData = &value | |||
case "TCPAbortOnClose": | |||
procNetstat.TcpExt.TCPAbortOnClose = value | |||
procNetstat.TcpExt.TCPAbortOnClose = &value | |||
case "TCPDeferAcceptDrop": | |||
procNetstat.TcpExt.TCPDeferAcceptDrop = value | |||
procNetstat.TcpExt.TCPDeferAcceptDrop = &value | |||
case "IPReversePathFilter": | |||
procNetstat.TcpExt.IPReversePathFilter = value | |||
procNetstat.TcpExt.IPReversePathFilter = &value | |||
case "TCPTimeWaitOverflow": | |||
procNetstat.TcpExt.TCPTimeWaitOverflow = value | |||
procNetstat.TcpExt.TCPTimeWaitOverflow = &value | |||
case "TCPReqQFullDoCookies": | |||
procNetstat.TcpExt.TCPReqQFullDoCookies = value | |||
procNetstat.TcpExt.TCPReqQFullDoCookies = &value | |||
case "TCPReqQFullDrop": | |||
procNetstat.TcpExt.TCPReqQFullDrop = value | |||
procNetstat.TcpExt.TCPReqQFullDrop = &value | |||
case "TCPRetransFail": | |||
procNetstat.TcpExt.TCPRetransFail = value | |||
procNetstat.TcpExt.TCPRetransFail = &value | |||
case "TCPRcvCoalesce": | |||
procNetstat.TcpExt.TCPRcvCoalesce = value | |||
procNetstat.TcpExt.TCPRcvCoalesce = &value | |||
case "TCPRcvQDrop": | |||
procNetstat.TcpExt.TCPRcvQDrop = &value | |||
case "TCPOFOQueue": | |||
procNetstat.TcpExt.TCPOFOQueue = value | |||
procNetstat.TcpExt.TCPOFOQueue = &value | |||
case "TCPOFODrop": | |||
procNetstat.TcpExt.TCPOFODrop = value | |||
procNetstat.TcpExt.TCPOFODrop = &value | |||
case "TCPOFOMerge": | |||
procNetstat.TcpExt.TCPOFOMerge = value | |||
procNetstat.TcpExt.TCPOFOMerge = &value | |||
case "TCPChallengeACK": | |||
procNetstat.TcpExt.TCPChallengeACK = value | |||
procNetstat.TcpExt.TCPChallengeACK = &value | |||
case "TCPSYNChallenge": | |||
procNetstat.TcpExt.TCPSYNChallenge = value | |||
procNetstat.TcpExt.TCPSYNChallenge = &value | |||
case "TCPFastOpenActive": | |||
procNetstat.TcpExt.TCPFastOpenActive = value | |||
procNetstat.TcpExt.TCPFastOpenActive = &value | |||
case "TCPFastOpenActiveFail": | |||
procNetstat.TcpExt.TCPFastOpenActiveFail = value | |||
procNetstat.TcpExt.TCPFastOpenActiveFail = &value | |||
case "TCPFastOpenPassive": | |||
procNetstat.TcpExt.TCPFastOpenPassive = value | |||
procNetstat.TcpExt.TCPFastOpenPassive = &value | |||
case "TCPFastOpenPassiveFail": | |||
procNetstat.TcpExt.TCPFastOpenPassiveFail = value | |||
procNetstat.TcpExt.TCPFastOpenPassiveFail = &value | |||
case "TCPFastOpenListenOverflow": | |||
procNetstat.TcpExt.TCPFastOpenListenOverflow = value | |||
procNetstat.TcpExt.TCPFastOpenListenOverflow = &value | |||
case "TCPFastOpenCookieReqd": | |||
procNetstat.TcpExt.TCPFastOpenCookieReqd = value | |||
procNetstat.TcpExt.TCPFastOpenCookieReqd = &value | |||
case "TCPFastOpenBlackhole": | |||
procNetstat.TcpExt.TCPFastOpenBlackhole = value | |||
procNetstat.TcpExt.TCPFastOpenBlackhole = &value | |||
case "TCPSpuriousRtxHostQueues": | |||
procNetstat.TcpExt.TCPSpuriousRtxHostQueues = value | |||
procNetstat.TcpExt.TCPSpuriousRtxHostQueues = &value | |||
case "BusyPollRxPackets": | |||
procNetstat.TcpExt.BusyPollRxPackets = value | |||
procNetstat.TcpExt.BusyPollRxPackets = &value | |||
case "TCPAutoCorking": | |||
procNetstat.TcpExt.TCPAutoCorking = value | |||
procNetstat.TcpExt.TCPAutoCorking = &value | |||
case "TCPFromZeroWindowAdv": | |||
procNetstat.TcpExt.TCPFromZeroWindowAdv = value | |||
procNetstat.TcpExt.TCPFromZeroWindowAdv = &value | |||
case "TCPToZeroWindowAdv": | |||
procNetstat.TcpExt.TCPToZeroWindowAdv = value | |||
procNetstat.TcpExt.TCPToZeroWindowAdv = &value | |||
case "TCPWantZeroWindowAdv": | |||
procNetstat.TcpExt.TCPWantZeroWindowAdv = value | |||
procNetstat.TcpExt.TCPWantZeroWindowAdv = &value | |||
case "TCPSynRetrans": | |||
procNetstat.TcpExt.TCPSynRetrans = value | |||
procNetstat.TcpExt.TCPSynRetrans = &value | |||
case "TCPOrigDataSent": | |||
procNetstat.TcpExt.TCPOrigDataSent = value | |||
procNetstat.TcpExt.TCPOrigDataSent = &value | |||
case "TCPHystartTrainDetect": | |||
procNetstat.TcpExt.TCPHystartTrainDetect = value | |||
procNetstat.TcpExt.TCPHystartTrainDetect = &value | |||
case "TCPHystartTrainCwnd": | |||
procNetstat.TcpExt.TCPHystartTrainCwnd = value | |||
procNetstat.TcpExt.TCPHystartTrainCwnd = &value | |||
case "TCPHystartDelayDetect": | |||
procNetstat.TcpExt.TCPHystartDelayDetect = value | |||
procNetstat.TcpExt.TCPHystartDelayDetect = &value | |||
case "TCPHystartDelayCwnd": | |||
procNetstat.TcpExt.TCPHystartDelayCwnd = value | |||
procNetstat.TcpExt.TCPHystartDelayCwnd = &value | |||
case "TCPACKSkippedSynRecv": | |||
procNetstat.TcpExt.TCPACKSkippedSynRecv = value | |||
procNetstat.TcpExt.TCPACKSkippedSynRecv = &value | |||
case "TCPACKSkippedPAWS": | |||
procNetstat.TcpExt.TCPACKSkippedPAWS = value | |||
procNetstat.TcpExt.TCPACKSkippedPAWS = &value | |||
case "TCPACKSkippedSeq": | |||
procNetstat.TcpExt.TCPACKSkippedSeq = value | |||
procNetstat.TcpExt.TCPACKSkippedSeq = &value | |||
case "TCPACKSkippedFinWait2": | |||
procNetstat.TcpExt.TCPACKSkippedFinWait2 = value | |||
procNetstat.TcpExt.TCPACKSkippedFinWait2 = &value | |||
case "TCPACKSkippedTimeWait": | |||
procNetstat.TcpExt.TCPACKSkippedTimeWait = value | |||
procNetstat.TcpExt.TCPACKSkippedTimeWait = &value | |||
case "TCPACKSkippedChallenge": | |||
procNetstat.TcpExt.TCPACKSkippedChallenge = value | |||
procNetstat.TcpExt.TCPACKSkippedChallenge = &value | |||
case "TCPWinProbe": | |||
procNetstat.TcpExt.TCPWinProbe = value | |||
procNetstat.TcpExt.TCPWinProbe = &value | |||
case "TCPKeepAlive": | |||
procNetstat.TcpExt.TCPKeepAlive = value | |||
procNetstat.TcpExt.TCPKeepAlive = &value | |||
case "TCPMTUPFail": | |||
procNetstat.TcpExt.TCPMTUPFail = value | |||
procNetstat.TcpExt.TCPMTUPFail = &value | |||
case "TCPMTUPSuccess": | |||
procNetstat.TcpExt.TCPMTUPSuccess = value | |||
procNetstat.TcpExt.TCPMTUPSuccess = &value | |||
case "TCPWqueueTooBig": | |||
procNetstat.TcpExt.TCPWqueueTooBig = value | |||
procNetstat.TcpExt.TCPWqueueTooBig = &value | |||
} | |||
case "IpExt": | |||
switch key { | |||
case "InNoRoutes": | |||
procNetstat.IpExt.InNoRoutes = value | |||
procNetstat.IpExt.InNoRoutes = &value | |||
case "InTruncatedPkts": | |||
procNetstat.IpExt.InTruncatedPkts = value | |||
procNetstat.IpExt.InTruncatedPkts = &value | |||
case "InMcastPkts": | |||
procNetstat.IpExt.InMcastPkts = value | |||
procNetstat.IpExt.InMcastPkts = &value | |||
case "OutMcastPkts": | |||
procNetstat.IpExt.OutMcastPkts = value | |||
procNetstat.IpExt.OutMcastPkts = &value | |||
case "InBcastPkts": | |||
procNetstat.IpExt.InBcastPkts = value | |||
procNetstat.IpExt.InBcastPkts = &value | |||
case "OutBcastPkts": | |||
procNetstat.IpExt.OutBcastPkts = value | |||
procNetstat.IpExt.OutBcastPkts = &value | |||
case "InOctets": | |||
procNetstat.IpExt.InOctets = value | |||
procNetstat.IpExt.InOctets = &value | |||
case "OutOctets": | |||
procNetstat.IpExt.OutOctets = value | |||
procNetstat.IpExt.OutOctets = &value | |||
case "InMcastOctets": | |||
procNetstat.IpExt.InMcastOctets = value | |||
procNetstat.IpExt.InMcastOctets = &value | |||
case "OutMcastOctets": | |||
procNetstat.IpExt.OutMcastOctets = value | |||
procNetstat.IpExt.OutMcastOctets = &value | |||
case "InBcastOctets": | |||
procNetstat.IpExt.InBcastOctets = value | |||
procNetstat.IpExt.InBcastOctets = &value | |||
case "OutBcastOctets": | |||
procNetstat.IpExt.OutBcastOctets = value | |||
procNetstat.IpExt.OutBcastOctets = &value | |||
case "InCsumErrors": | |||
procNetstat.IpExt.InCsumErrors = value | |||
procNetstat.IpExt.InCsumErrors = &value | |||
case "InNoECTPkts": | |||
procNetstat.IpExt.InNoECTPkts = value | |||
procNetstat.IpExt.InNoECTPkts = &value | |||
case "InECT1Pkts": | |||
procNetstat.IpExt.InECT1Pkts = value | |||
procNetstat.IpExt.InECT1Pkts = &value | |||
case "InECT0Pkts": | |||
procNetstat.IpExt.InECT0Pkts = value | |||
procNetstat.IpExt.InECT0Pkts = &value | |||
case "InCEPkts": | |||
procNetstat.IpExt.InCEPkts = value | |||
procNetstat.IpExt.InCEPkts = &value | |||
case "ReasmOverlaps": | |||
procNetstat.IpExt.ReasmOverlaps = value | |||
procNetstat.IpExt.ReasmOverlaps = &value | |||
} | |||
} | |||
} | |||
@@ -37,100 +37,100 @@ type ProcSnmp struct { | |||
} | |||
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 { | |||
InType3 float64 | |||
OutType3 float64 | |||
InType3 *float64 | |||
OutType3 *float64 | |||
} | |||
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 | |||
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 | |||
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) { | |||
@@ -173,178 +173,178 @@ func parseSnmp(r io.Reader, fileName string) (ProcSnmp, error) { | |||
case "Ip": | |||
switch key { | |||
case "Forwarding": | |||
procSnmp.Ip.Forwarding = value | |||
procSnmp.Ip.Forwarding = &value | |||
case "DefaultTTL": | |||
procSnmp.Ip.DefaultTTL = value | |||
procSnmp.Ip.DefaultTTL = &value | |||
case "InReceives": | |||
procSnmp.Ip.InReceives = value | |||
procSnmp.Ip.InReceives = &value | |||
case "InHdrErrors": | |||
procSnmp.Ip.InHdrErrors = value | |||
procSnmp.Ip.InHdrErrors = &value | |||
case "InAddrErrors": | |||
procSnmp.Ip.InAddrErrors = value | |||
procSnmp.Ip.InAddrErrors = &value | |||
case "ForwDatagrams": | |||
procSnmp.Ip.ForwDatagrams = value | |||
procSnmp.Ip.ForwDatagrams = &value | |||
case "InUnknownProtos": | |||
procSnmp.Ip.InUnknownProtos = value | |||
procSnmp.Ip.InUnknownProtos = &value | |||
case "InDiscards": | |||
procSnmp.Ip.InDiscards = value | |||
procSnmp.Ip.InDiscards = &value | |||
case "InDelivers": | |||
procSnmp.Ip.InDelivers = value | |||
procSnmp.Ip.InDelivers = &value | |||
case "OutRequests": | |||
procSnmp.Ip.OutRequests = value | |||
procSnmp.Ip.OutRequests = &value | |||
case "OutDiscards": | |||
procSnmp.Ip.OutDiscards = value | |||
procSnmp.Ip.OutDiscards = &value | |||
case "OutNoRoutes": | |||
procSnmp.Ip.OutNoRoutes = value | |||
procSnmp.Ip.OutNoRoutes = &value | |||
case "ReasmTimeout": | |||
procSnmp.Ip.ReasmTimeout = value | |||
procSnmp.Ip.ReasmTimeout = &value | |||
case "ReasmReqds": | |||
procSnmp.Ip.ReasmReqds = value | |||
procSnmp.Ip.ReasmReqds = &value | |||
case "ReasmOKs": | |||
procSnmp.Ip.ReasmOKs = value | |||
procSnmp.Ip.ReasmOKs = &value | |||
case "ReasmFails": | |||
procSnmp.Ip.ReasmFails = value | |||
procSnmp.Ip.ReasmFails = &value | |||
case "FragOKs": | |||
procSnmp.Ip.FragOKs = value | |||
procSnmp.Ip.FragOKs = &value | |||
case "FragFails": | |||
procSnmp.Ip.FragFails = value | |||
procSnmp.Ip.FragFails = &value | |||
case "FragCreates": | |||
procSnmp.Ip.FragCreates = value | |||
procSnmp.Ip.FragCreates = &value | |||
} | |||
case "Icmp": | |||
switch key { | |||
case "InMsgs": | |||
procSnmp.Icmp.InMsgs = value | |||
procSnmp.Icmp.InMsgs = &value | |||
case "InErrors": | |||
procSnmp.Icmp.InErrors = value | |||
procSnmp.Icmp.InErrors = &value | |||
case "InCsumErrors": | |||
procSnmp.Icmp.InCsumErrors = value | |||
procSnmp.Icmp.InCsumErrors = &value | |||
case "InDestUnreachs": | |||
procSnmp.Icmp.InDestUnreachs = value | |||
procSnmp.Icmp.InDestUnreachs = &value | |||
case "InTimeExcds": | |||
procSnmp.Icmp.InTimeExcds = value | |||
procSnmp.Icmp.InTimeExcds = &value | |||
case "InParmProbs": | |||
procSnmp.Icmp.InParmProbs = value | |||
procSnmp.Icmp.InParmProbs = &value | |||
case "InSrcQuenchs": | |||
procSnmp.Icmp.InSrcQuenchs = value | |||
procSnmp.Icmp.InSrcQuenchs = &value | |||
case "InRedirects": | |||
procSnmp.Icmp.InRedirects = value | |||
procSnmp.Icmp.InRedirects = &value | |||
case "InEchos": | |||
procSnmp.Icmp.InEchos = value | |||
procSnmp.Icmp.InEchos = &value | |||
case "InEchoReps": | |||
procSnmp.Icmp.InEchoReps = value | |||
procSnmp.Icmp.InEchoReps = &value | |||
case "InTimestamps": | |||
procSnmp.Icmp.InTimestamps = value | |||
procSnmp.Icmp.InTimestamps = &value | |||
case "InTimestampReps": | |||
procSnmp.Icmp.InTimestampReps = value | |||
procSnmp.Icmp.InTimestampReps = &value | |||
case "InAddrMasks": | |||
procSnmp.Icmp.InAddrMasks = value | |||
procSnmp.Icmp.InAddrMasks = &value | |||
case "InAddrMaskReps": | |||
procSnmp.Icmp.InAddrMaskReps = value | |||
procSnmp.Icmp.InAddrMaskReps = &value | |||
case "OutMsgs": | |||
procSnmp.Icmp.OutMsgs = value | |||
procSnmp.Icmp.OutMsgs = &value | |||
case "OutErrors": | |||
procSnmp.Icmp.OutErrors = value | |||
procSnmp.Icmp.OutErrors = &value | |||
case "OutDestUnreachs": | |||
procSnmp.Icmp.OutDestUnreachs = value | |||
procSnmp.Icmp.OutDestUnreachs = &value | |||
case "OutTimeExcds": | |||
procSnmp.Icmp.OutTimeExcds = value | |||
procSnmp.Icmp.OutTimeExcds = &value | |||
case "OutParmProbs": | |||
procSnmp.Icmp.OutParmProbs = value | |||
procSnmp.Icmp.OutParmProbs = &value | |||
case "OutSrcQuenchs": | |||
procSnmp.Icmp.OutSrcQuenchs = value | |||
procSnmp.Icmp.OutSrcQuenchs = &value | |||
case "OutRedirects": | |||
procSnmp.Icmp.OutRedirects = value | |||
procSnmp.Icmp.OutRedirects = &value | |||
case "OutEchos": | |||
procSnmp.Icmp.OutEchos = value | |||
procSnmp.Icmp.OutEchos = &value | |||
case "OutEchoReps": | |||
procSnmp.Icmp.OutEchoReps = value | |||
procSnmp.Icmp.OutEchoReps = &value | |||
case "OutTimestamps": | |||
procSnmp.Icmp.OutTimestamps = value | |||
procSnmp.Icmp.OutTimestamps = &value | |||
case "OutTimestampReps": | |||
procSnmp.Icmp.OutTimestampReps = value | |||
procSnmp.Icmp.OutTimestampReps = &value | |||
case "OutAddrMasks": | |||
procSnmp.Icmp.OutAddrMasks = value | |||
procSnmp.Icmp.OutAddrMasks = &value | |||
case "OutAddrMaskReps": | |||
procSnmp.Icmp.OutAddrMaskReps = value | |||
procSnmp.Icmp.OutAddrMaskReps = &value | |||
} | |||
case "IcmpMsg": | |||
switch key { | |||
case "InType3": | |||
procSnmp.IcmpMsg.InType3 = value | |||
procSnmp.IcmpMsg.InType3 = &value | |||
case "OutType3": | |||
procSnmp.IcmpMsg.OutType3 = value | |||
procSnmp.IcmpMsg.OutType3 = &value | |||
} | |||
case "Tcp": | |||
switch key { | |||
case "RtoAlgorithm": | |||
procSnmp.Tcp.RtoAlgorithm = value | |||
procSnmp.Tcp.RtoAlgorithm = &value | |||
case "RtoMin": | |||
procSnmp.Tcp.RtoMin = value | |||
procSnmp.Tcp.RtoMin = &value | |||
case "RtoMax": | |||
procSnmp.Tcp.RtoMax = value | |||
procSnmp.Tcp.RtoMax = &value | |||
case "MaxConn": | |||
procSnmp.Tcp.MaxConn = value | |||
procSnmp.Tcp.MaxConn = &value | |||
case "ActiveOpens": | |||
procSnmp.Tcp.ActiveOpens = value | |||
procSnmp.Tcp.ActiveOpens = &value | |||
case "PassiveOpens": | |||
procSnmp.Tcp.PassiveOpens = value | |||
procSnmp.Tcp.PassiveOpens = &value | |||
case "AttemptFails": | |||
procSnmp.Tcp.AttemptFails = value | |||
procSnmp.Tcp.AttemptFails = &value | |||
case "EstabResets": | |||
procSnmp.Tcp.EstabResets = value | |||
procSnmp.Tcp.EstabResets = &value | |||
case "CurrEstab": | |||
procSnmp.Tcp.CurrEstab = value | |||
procSnmp.Tcp.CurrEstab = &value | |||
case "InSegs": | |||
procSnmp.Tcp.InSegs = value | |||
procSnmp.Tcp.InSegs = &value | |||
case "OutSegs": | |||
procSnmp.Tcp.OutSegs = value | |||
procSnmp.Tcp.OutSegs = &value | |||
case "RetransSegs": | |||
procSnmp.Tcp.RetransSegs = value | |||
procSnmp.Tcp.RetransSegs = &value | |||
case "InErrs": | |||
procSnmp.Tcp.InErrs = value | |||
procSnmp.Tcp.InErrs = &value | |||
case "OutRsts": | |||
procSnmp.Tcp.OutRsts = value | |||
procSnmp.Tcp.OutRsts = &value | |||
case "InCsumErrors": | |||
procSnmp.Tcp.InCsumErrors = value | |||
procSnmp.Tcp.InCsumErrors = &value | |||
} | |||
case "Udp": | |||
switch key { | |||
case "InDatagrams": | |||
procSnmp.Udp.InDatagrams = value | |||
procSnmp.Udp.InDatagrams = &value | |||
case "NoPorts": | |||
procSnmp.Udp.NoPorts = value | |||
procSnmp.Udp.NoPorts = &value | |||
case "InErrors": | |||
procSnmp.Udp.InErrors = value | |||
procSnmp.Udp.InErrors = &value | |||
case "OutDatagrams": | |||
procSnmp.Udp.OutDatagrams = value | |||
procSnmp.Udp.OutDatagrams = &value | |||
case "RcvbufErrors": | |||
procSnmp.Udp.RcvbufErrors = value | |||
procSnmp.Udp.RcvbufErrors = &value | |||
case "SndbufErrors": | |||
procSnmp.Udp.SndbufErrors = value | |||
procSnmp.Udp.SndbufErrors = &value | |||
case "InCsumErrors": | |||
procSnmp.Udp.InCsumErrors = value | |||
procSnmp.Udp.InCsumErrors = &value | |||
case "IgnoredMulti": | |||
procSnmp.Udp.IgnoredMulti = value | |||
procSnmp.Udp.IgnoredMulti = &value | |||
} | |||
case "UdpLite": | |||
switch key { | |||
case "InDatagrams": | |||
procSnmp.UdpLite.InDatagrams = value | |||
procSnmp.UdpLite.InDatagrams = &value | |||
case "NoPorts": | |||
procSnmp.UdpLite.NoPorts = value | |||
procSnmp.UdpLite.NoPorts = &value | |||
case "InErrors": | |||
procSnmp.UdpLite.InErrors = value | |||
procSnmp.UdpLite.InErrors = &value | |||
case "OutDatagrams": | |||
procSnmp.UdpLite.OutDatagrams = value | |||
procSnmp.UdpLite.OutDatagrams = &value | |||
case "RcvbufErrors": | |||
procSnmp.UdpLite.RcvbufErrors = value | |||
procSnmp.UdpLite.RcvbufErrors = &value | |||
case "SndbufErrors": | |||
procSnmp.UdpLite.SndbufErrors = value | |||
procSnmp.UdpLite.SndbufErrors = &value | |||
case "InCsumErrors": | |||
procSnmp.UdpLite.InCsumErrors = value | |||
procSnmp.UdpLite.InCsumErrors = &value | |||
case "IgnoredMulti": | |||
procSnmp.UdpLite.IgnoredMulti = value | |||
procSnmp.UdpLite.IgnoredMulti = &value | |||
} | |||
} | |||
} | |||
@@ -36,106 +36,106 @@ type ProcSnmp6 struct { | |||
} | |||
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 { | |||
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 | |||
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 | |||
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) { | |||
@@ -182,197 +182,197 @@ func parseSNMP6Stats(r io.Reader) (ProcSnmp6, error) { | |||
case "Ip6": | |||
switch key { | |||
case "InReceives": | |||
procSnmp6.Ip6.InReceives = value | |||
procSnmp6.Ip6.InReceives = &value | |||
case "InHdrErrors": | |||
procSnmp6.Ip6.InHdrErrors = value | |||
procSnmp6.Ip6.InHdrErrors = &value | |||
case "InTooBigErrors": | |||
procSnmp6.Ip6.InTooBigErrors = value | |||
procSnmp6.Ip6.InTooBigErrors = &value | |||
case "InNoRoutes": | |||
procSnmp6.Ip6.InNoRoutes = value | |||
procSnmp6.Ip6.InNoRoutes = &value | |||
case "InAddrErrors": | |||
procSnmp6.Ip6.InAddrErrors = value | |||
procSnmp6.Ip6.InAddrErrors = &value | |||
case "InUnknownProtos": | |||
procSnmp6.Ip6.InUnknownProtos = value | |||
procSnmp6.Ip6.InUnknownProtos = &value | |||
case "InTruncatedPkts": | |||
procSnmp6.Ip6.InTruncatedPkts = value | |||
procSnmp6.Ip6.InTruncatedPkts = &value | |||
case "InDiscards": | |||
procSnmp6.Ip6.InDiscards = value | |||
procSnmp6.Ip6.InDiscards = &value | |||
case "InDelivers": | |||
procSnmp6.Ip6.InDelivers = value | |||
procSnmp6.Ip6.InDelivers = &value | |||
case "OutForwDatagrams": | |||
procSnmp6.Ip6.OutForwDatagrams = value | |||
procSnmp6.Ip6.OutForwDatagrams = &value | |||
case "OutRequests": | |||
procSnmp6.Ip6.OutRequests = value | |||
procSnmp6.Ip6.OutRequests = &value | |||
case "OutDiscards": | |||
procSnmp6.Ip6.OutDiscards = value | |||
procSnmp6.Ip6.OutDiscards = &value | |||
case "OutNoRoutes": | |||
procSnmp6.Ip6.OutNoRoutes = value | |||
procSnmp6.Ip6.OutNoRoutes = &value | |||
case "ReasmTimeout": | |||
procSnmp6.Ip6.ReasmTimeout = value | |||
procSnmp6.Ip6.ReasmTimeout = &value | |||
case "ReasmReqds": | |||
procSnmp6.Ip6.ReasmReqds = value | |||
procSnmp6.Ip6.ReasmReqds = &value | |||
case "ReasmOKs": | |||
procSnmp6.Ip6.ReasmOKs = value | |||
procSnmp6.Ip6.ReasmOKs = &value | |||
case "ReasmFails": | |||
procSnmp6.Ip6.ReasmFails = value | |||
procSnmp6.Ip6.ReasmFails = &value | |||
case "FragOKs": | |||
procSnmp6.Ip6.FragOKs = value | |||
procSnmp6.Ip6.FragOKs = &value | |||
case "FragFails": | |||
procSnmp6.Ip6.FragFails = value | |||
procSnmp6.Ip6.FragFails = &value | |||
case "FragCreates": | |||
procSnmp6.Ip6.FragCreates = value | |||
procSnmp6.Ip6.FragCreates = &value | |||
case "InMcastPkts": | |||
procSnmp6.Ip6.InMcastPkts = value | |||
procSnmp6.Ip6.InMcastPkts = &value | |||
case "OutMcastPkts": | |||
procSnmp6.Ip6.OutMcastPkts = value | |||
procSnmp6.Ip6.OutMcastPkts = &value | |||
case "InOctets": | |||
procSnmp6.Ip6.InOctets = value | |||
procSnmp6.Ip6.InOctets = &value | |||
case "OutOctets": | |||
procSnmp6.Ip6.OutOctets = value | |||
procSnmp6.Ip6.OutOctets = &value | |||
case "InMcastOctets": | |||
procSnmp6.Ip6.InMcastOctets = value | |||
procSnmp6.Ip6.InMcastOctets = &value | |||
case "OutMcastOctets": | |||
procSnmp6.Ip6.OutMcastOctets = value | |||
procSnmp6.Ip6.OutMcastOctets = &value | |||
case "InBcastOctets": | |||
procSnmp6.Ip6.InBcastOctets = value | |||
procSnmp6.Ip6.InBcastOctets = &value | |||
case "OutBcastOctets": | |||
procSnmp6.Ip6.OutBcastOctets = value | |||
procSnmp6.Ip6.OutBcastOctets = &value | |||
case "InNoECTPkts": | |||
procSnmp6.Ip6.InNoECTPkts = value | |||
procSnmp6.Ip6.InNoECTPkts = &value | |||
case "InECT1Pkts": | |||
procSnmp6.Ip6.InECT1Pkts = value | |||
procSnmp6.Ip6.InECT1Pkts = &value | |||
case "InECT0Pkts": | |||
procSnmp6.Ip6.InECT0Pkts = value | |||
procSnmp6.Ip6.InECT0Pkts = &value | |||
case "InCEPkts": | |||
procSnmp6.Ip6.InCEPkts = value | |||
procSnmp6.Ip6.InCEPkts = &value | |||
} | |||
case "Icmp6": | |||
switch key { | |||
case "InMsgs": | |||
procSnmp6.Icmp6.InMsgs = value | |||
procSnmp6.Icmp6.InMsgs = &value | |||
case "InErrors": | |||
procSnmp6.Icmp6.InErrors = value | |||
procSnmp6.Icmp6.InErrors = &value | |||
case "OutMsgs": | |||
procSnmp6.Icmp6.OutMsgs = value | |||
procSnmp6.Icmp6.OutMsgs = &value | |||
case "OutErrors": | |||
procSnmp6.Icmp6.OutErrors = value | |||
procSnmp6.Icmp6.OutErrors = &value | |||
case "InCsumErrors": | |||
procSnmp6.Icmp6.InCsumErrors = value | |||
procSnmp6.Icmp6.InCsumErrors = &value | |||
case "InDestUnreachs": | |||
procSnmp6.Icmp6.InDestUnreachs = value | |||
procSnmp6.Icmp6.InDestUnreachs = &value | |||
case "InPktTooBigs": | |||
procSnmp6.Icmp6.InPktTooBigs = value | |||
procSnmp6.Icmp6.InPktTooBigs = &value | |||
case "InTimeExcds": | |||
procSnmp6.Icmp6.InTimeExcds = value | |||
procSnmp6.Icmp6.InTimeExcds = &value | |||
case "InParmProblems": | |||
procSnmp6.Icmp6.InParmProblems = value | |||
procSnmp6.Icmp6.InParmProblems = &value | |||
case "InEchos": | |||
procSnmp6.Icmp6.InEchos = value | |||
procSnmp6.Icmp6.InEchos = &value | |||
case "InEchoReplies": | |||
procSnmp6.Icmp6.InEchoReplies = value | |||
procSnmp6.Icmp6.InEchoReplies = &value | |||
case "InGroupMembQueries": | |||
procSnmp6.Icmp6.InGroupMembQueries = value | |||
procSnmp6.Icmp6.InGroupMembQueries = &value | |||
case "InGroupMembResponses": | |||
procSnmp6.Icmp6.InGroupMembResponses = value | |||
procSnmp6.Icmp6.InGroupMembResponses = &value | |||
case "InGroupMembReductions": | |||
procSnmp6.Icmp6.InGroupMembReductions = value | |||
procSnmp6.Icmp6.InGroupMembReductions = &value | |||
case "InRouterSolicits": | |||
procSnmp6.Icmp6.InRouterSolicits = value | |||
procSnmp6.Icmp6.InRouterSolicits = &value | |||
case "InRouterAdvertisements": | |||
procSnmp6.Icmp6.InRouterAdvertisements = value | |||
procSnmp6.Icmp6.InRouterAdvertisements = &value | |||
case "InNeighborSolicits": | |||
procSnmp6.Icmp6.InNeighborSolicits = value | |||
procSnmp6.Icmp6.InNeighborSolicits = &value | |||
case "InNeighborAdvertisements": | |||
procSnmp6.Icmp6.InNeighborAdvertisements = value | |||
procSnmp6.Icmp6.InNeighborAdvertisements = &value | |||
case "InRedirects": | |||
procSnmp6.Icmp6.InRedirects = value | |||
procSnmp6.Icmp6.InRedirects = &value | |||
case "InMLDv2Reports": | |||
procSnmp6.Icmp6.InMLDv2Reports = value | |||
procSnmp6.Icmp6.InMLDv2Reports = &value | |||
case "OutDestUnreachs": | |||
procSnmp6.Icmp6.OutDestUnreachs = value | |||
procSnmp6.Icmp6.OutDestUnreachs = &value | |||
case "OutPktTooBigs": | |||
procSnmp6.Icmp6.OutPktTooBigs = value | |||
procSnmp6.Icmp6.OutPktTooBigs = &value | |||
case "OutTimeExcds": | |||
procSnmp6.Icmp6.OutTimeExcds = value | |||
procSnmp6.Icmp6.OutTimeExcds = &value | |||
case "OutParmProblems": | |||
procSnmp6.Icmp6.OutParmProblems = value | |||
procSnmp6.Icmp6.OutParmProblems = &value | |||
case "OutEchos": | |||
procSnmp6.Icmp6.OutEchos = value | |||
procSnmp6.Icmp6.OutEchos = &value | |||
case "OutEchoReplies": | |||
procSnmp6.Icmp6.OutEchoReplies = value | |||
procSnmp6.Icmp6.OutEchoReplies = &value | |||
case "OutGroupMembQueries": | |||
procSnmp6.Icmp6.OutGroupMembQueries = value | |||
procSnmp6.Icmp6.OutGroupMembQueries = &value | |||
case "OutGroupMembResponses": | |||
procSnmp6.Icmp6.OutGroupMembResponses = value | |||
procSnmp6.Icmp6.OutGroupMembResponses = &value | |||
case "OutGroupMembReductions": | |||
procSnmp6.Icmp6.OutGroupMembReductions = value | |||
procSnmp6.Icmp6.OutGroupMembReductions = &value | |||
case "OutRouterSolicits": | |||
procSnmp6.Icmp6.OutRouterSolicits = value | |||
procSnmp6.Icmp6.OutRouterSolicits = &value | |||
case "OutRouterAdvertisements": | |||
procSnmp6.Icmp6.OutRouterAdvertisements = value | |||
procSnmp6.Icmp6.OutRouterAdvertisements = &value | |||
case "OutNeighborSolicits": | |||
procSnmp6.Icmp6.OutNeighborSolicits = value | |||
procSnmp6.Icmp6.OutNeighborSolicits = &value | |||
case "OutNeighborAdvertisements": | |||
procSnmp6.Icmp6.OutNeighborAdvertisements = value | |||
procSnmp6.Icmp6.OutNeighborAdvertisements = &value | |||
case "OutRedirects": | |||
procSnmp6.Icmp6.OutRedirects = value | |||
procSnmp6.Icmp6.OutRedirects = &value | |||
case "OutMLDv2Reports": | |||
procSnmp6.Icmp6.OutMLDv2Reports = value | |||
procSnmp6.Icmp6.OutMLDv2Reports = &value | |||
case "InType1": | |||
procSnmp6.Icmp6.InType1 = value | |||
procSnmp6.Icmp6.InType1 = &value | |||
case "InType134": | |||
procSnmp6.Icmp6.InType134 = value | |||
procSnmp6.Icmp6.InType134 = &value | |||
case "InType135": | |||
procSnmp6.Icmp6.InType135 = value | |||
procSnmp6.Icmp6.InType135 = &value | |||
case "InType136": | |||
procSnmp6.Icmp6.InType136 = value | |||
procSnmp6.Icmp6.InType136 = &value | |||
case "InType143": | |||
procSnmp6.Icmp6.InType143 = value | |||
procSnmp6.Icmp6.InType143 = &value | |||
case "OutType133": | |||
procSnmp6.Icmp6.OutType133 = value | |||
procSnmp6.Icmp6.OutType133 = &value | |||
case "OutType135": | |||
procSnmp6.Icmp6.OutType135 = value | |||
procSnmp6.Icmp6.OutType135 = &value | |||
case "OutType136": | |||
procSnmp6.Icmp6.OutType136 = value | |||
procSnmp6.Icmp6.OutType136 = &value | |||
case "OutType143": | |||
procSnmp6.Icmp6.OutType143 = value | |||
procSnmp6.Icmp6.OutType143 = &value | |||
} | |||
case "Udp6": | |||
switch key { | |||
case "InDatagrams": | |||
procSnmp6.Udp6.InDatagrams = value | |||
procSnmp6.Udp6.InDatagrams = &value | |||
case "NoPorts": | |||
procSnmp6.Udp6.NoPorts = value | |||
procSnmp6.Udp6.NoPorts = &value | |||
case "InErrors": | |||
procSnmp6.Udp6.InErrors = value | |||
procSnmp6.Udp6.InErrors = &value | |||
case "OutDatagrams": | |||
procSnmp6.Udp6.OutDatagrams = value | |||
procSnmp6.Udp6.OutDatagrams = &value | |||
case "RcvbufErrors": | |||
procSnmp6.Udp6.RcvbufErrors = value | |||
procSnmp6.Udp6.RcvbufErrors = &value | |||
case "SndbufErrors": | |||
procSnmp6.Udp6.SndbufErrors = value | |||
procSnmp6.Udp6.SndbufErrors = &value | |||
case "InCsumErrors": | |||
procSnmp6.Udp6.InCsumErrors = value | |||
procSnmp6.Udp6.InCsumErrors = &value | |||
case "IgnoredMulti": | |||
procSnmp6.Udp6.IgnoredMulti = value | |||
procSnmp6.Udp6.IgnoredMulti = &value | |||
} | |||
case "UdpLite6": | |||
switch key { | |||
case "InDatagrams": | |||
procSnmp6.UdpLite6.InDatagrams = value | |||
procSnmp6.UdpLite6.InDatagrams = &value | |||
case "NoPorts": | |||
procSnmp6.UdpLite6.NoPorts = value | |||
procSnmp6.UdpLite6.NoPorts = &value | |||
case "InErrors": | |||
procSnmp6.UdpLite6.InErrors = value | |||
procSnmp6.UdpLite6.InErrors = &value | |||
case "OutDatagrams": | |||
procSnmp6.UdpLite6.OutDatagrams = value | |||
procSnmp6.UdpLite6.OutDatagrams = &value | |||
case "RcvbufErrors": | |||
procSnmp6.UdpLite6.RcvbufErrors = value | |||
procSnmp6.UdpLite6.RcvbufErrors = &value | |||
case "SndbufErrors": | |||
procSnmp6.UdpLite6.SndbufErrors = value | |||
procSnmp6.UdpLite6.SndbufErrors = &value | |||
case "InCsumErrors": | |||
procSnmp6.UdpLite6.InCsumErrors = value | |||
procSnmp6.UdpLite6.InCsumErrors = &value | |||
} | |||
} | |||
} | |||
@@ -102,6 +102,8 @@ type ProcStat struct { | |||
RSS int | |||
// Soft limit in bytes on the rss of the process. | |||
RSSLimit uint64 | |||
// CPU number last executed on. | |||
Processor uint | |||
// 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. | |||
RTPriority uint | |||
@@ -184,7 +186,7 @@ func (p Proc) Stat() (ProcStat, error) { | |||
&ignoreUint64, | |||
&ignoreUint64, | |||
&ignoreInt64, | |||
&ignoreInt64, | |||
&s.Processor, | |||
&s.RTPriority, | |||
&s.Policy, | |||
&s.DelayAcctBlkIOTicks, | |||
@@ -96,10 +96,10 @@ func (p Proc) NewStatus() (ProcStatus, error) { | |||
kv := strings.SplitN(line, ":", 2) | |||
// 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" | |||
v = string(bytes.Trim([]byte(v), " kB")) | |||
v = strings.TrimSuffix(v, " kB") | |||
// value to int when possible | |||
// 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. | |||
CPUTotal CPUStat | |||
// Per-CPU statistics. | |||
CPU []CPUStat | |||
CPU map[int64]CPUStat | |||
// Number of times interrupts were handled, which contains numbered and unnumbered IRQs. | |||
IRQTotal uint64 | |||
// Number of times a numbered IRQ was triggered. | |||
@@ -170,10 +170,23 @@ func (fs FS) Stat() (Stat, error) { | |||
if err != nil { | |||
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() { | |||
line := scanner.Text() | |||
parts := strings.Fields(scanner.Text()) | |||
@@ -228,9 +241,6 @@ func (fs FS) Stat() (Stat, error) { | |||
if cpuID == -1 { | |||
stat.CPUTotal = cpuStat | |||
} else { | |||
for int64(len(stat.CPU)) <= cpuID { | |||
stat.CPU = append(stat.CPU, 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 | |||
// 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 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. | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// 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 | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// 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 | |||
@@ -2,8 +2,8 @@ | |||
// Use of this source code is governed by a BSD-style | |||
// 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 <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 | |||
// 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 | |||
import ( | |||
"runtime" | |||
"unsafe" | |||
) | |||
@@ -27,7 +26,7 @@ func IoctlSetInt(fd int, req uint, value int) error { | |||
// passing the integer value directly. | |||
func IoctlSetPointerInt(fd int, req uint, value int) error { | |||
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. | |||
@@ -36,9 +35,7 @@ func IoctlSetPointerInt(fd int, req uint, value int) error { | |||
func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||
// TODO: if we get the chance, remove the req parameter and | |||
// 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. | |||
@@ -46,9 +43,7 @@ func IoctlSetWinsize(fd int, req uint, value *Winsize) error { | |||
// The req value will usually be TCSETA or TIOCSETA. | |||
func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||
// 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 | |||
@@ -58,18 +53,18 @@ func IoctlSetTermios(fd int, req uint, value *Termios) error { | |||
// for those, IoctlRetInt should be used instead of this function. | |||
func IoctlGetInt(fd int, req uint) (int, error) { | |||
var value int | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return value, err | |||
} | |||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | |||
var value Winsize | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
func IoctlGetTermios(fd int, req uint) (*Termios, error) { | |||
var value Termios | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return &value, err | |||
} |
@@ -17,25 +17,23 @@ import ( | |||
// IoctlSetInt performs an ioctl operation which sets an integer value | |||
// 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)) | |||
} | |||
// 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 uint, value *Winsize) error { | |||
func IoctlSetWinsize(fd int, req int, value *Winsize) error { | |||
// TODO: if we get the chance, remove the req parameter and | |||
// 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. | |||
// | |||
// 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) { | |||
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; | |||
// 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 | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return value, err | |||
} | |||
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) { | |||
func IoctlGetWinsize(fd int, req int) (*Winsize, error) { | |||
var value Winsize | |||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) | |||
err := ioctlPtr(fd, req, unsafe.Pointer(&value)) | |||
return &value, err | |||
} | |||
// IoctlGetTermios performs an ioctl on fd with a *Termios. | |||
// | |||
// 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 | |||
if req != TCGETS { | |||
return &value, ENOSYS | |||
@@ -174,10 +174,10 @@ openbsd_arm64) | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
;; | |||
openbsd_mips64) | |||
mkasm="go run mkasm.go" | |||
mkerrors="$mkerrors -m64" | |||
mksyscall="go run mksyscall.go -openbsd" | |||
mksyscall="go run mksyscall.go -openbsd -libc" | |||
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 | |||
# API consistent across platforms. | |||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char" | |||
@@ -66,6 +66,7 @@ includes_Darwin=' | |||
#include <sys/ptrace.h> | |||
#include <sys/select.h> | |||
#include <sys/socket.h> | |||
#include <sys/stat.h> | |||
#include <sys/un.h> | |||
#include <sys/sockio.h> | |||
#include <sys/sys_domain.h> | |||
@@ -203,6 +204,7 @@ struct ltchars { | |||
#include <sys/timerfd.h> | |||
#include <sys/uio.h> | |||
#include <sys/xattr.h> | |||
#include <netinet/udp.h> | |||
#include <linux/audit.h> | |||
#include <linux/bpf.h> | |||
#include <linux/can.h> | |||
@@ -517,10 +519,11 @@ ccflags="$@" | |||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ || | |||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ || | |||
$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_.*_(MAX)?SIZE$/ || | |||
$2 ~ /^RAW_PAYLOAD_/ || | |||
$2 ~ /^[US]F_/ || | |||
$2 ~ /^TP_STATUS_/ || | |||
$2 ~ /^FALLOC_/ || | |||
$2 ~ /^ICMPV?6?_(FILTER|SEC)/ || | |||
@@ -7,6 +7,12 @@ | |||
package unix | |||
import "unsafe" | |||
func ptrace(request int, pid int, addr uintptr, data uintptr) error { | |||
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 | |||
import "unsafe" | |||
func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { | |||
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 | |||
} | |||
// 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) { | |||
h := (*Cmsghdr)(unsafe.Pointer(&b[0])) | |||
if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) { | |||
@@ -292,9 +292,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { | |||
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 | |||
case AF_INET: | |||
@@ -410,7 +408,8 @@ func (w WaitStatus) CoreDump() bool { return w&0x80 == 0x80 } | |||
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 | |||
// There is no way to create a custom fcntl and to keep //sys fcntl easily, | |||
@@ -8,7 +8,6 @@ | |||
package unix | |||
//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 mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) | |||
@@ -8,7 +8,6 @@ | |||
package unix | |||
//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 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 | |||
} | |||
} | |||
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 | |||
case AF_INET: | |||
@@ -14,7 +14,6 @@ package unix | |||
import ( | |||
"fmt" | |||
"runtime" | |||
"syscall" | |||
"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 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) | |||
@@ -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) } | |||
//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 { | |||
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. | |||
@@ -393,16 +392,14 @@ type IfreqMTU struct { | |||
func IoctlGetIfreqMTU(fd int, ifname string) (*IfreqMTU, error) { | |||
var ifreq IfreqMTU | |||
copy(ifreq.Name[:], ifname) | |||
err := ioctl(fd, SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) | |||
err := ioctlPtr(fd, SIOCGIFMTU, unsafe.Pointer(&ifreq)) | |||
return &ifreq, err | |||
} | |||
// IoctlSetIfreqMTU performs the SIOCSIFMTU ioctl operation on fd to set the MTU | |||
// of the network device specified by ifreq.Name. | |||
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 | |||
@@ -616,6 +613,7 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { | |||
//sys Rmdir(path string) (err error) | |||
//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 Setattrlist(path string, attrlist *Attrlist, attrBuf []byte, options int) (err error) | |||
//sys Setegid(egid int) (err error) | |||
//sysnb Seteuid(euid 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) | |||
//sysnb Setregid(rgid int, egid 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 Settimeofday(tp *Timeval) (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_portset | |||
// Getattrlist | |||
// Setattrlist | |||
// Getdirentriesattr | |||
// Searchfs | |||
// 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 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 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 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 Lstat(path string, stat *Stat_t) (err error) | |||
//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 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 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 | |||
@@ -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 Chown(path string, uid int, gid int) (err error) | |||
//sys Chroot(path string) (err error) | |||
//sys ClockGettime(clockid int32, time *Timespec) (err error) | |||
//sys Close(fd int) (err error) | |||
//sys Dup(fd int) (nfd 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 Setresgid(rgid int, egid int, sgid 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 Settimeofday(tp *Timeval) (err error) | |||
//sysnb Setuid(uid int) (err error) | |||
@@ -161,7 +161,8 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { | |||
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 | |||
@@ -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 ptracePtr(request int, pid int, addr unsafe.Pointer, data int) (err error) = SYS_PTRACE | |||
func PtraceAttach(pid int) (err error) { | |||
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) { | |||
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) { | |||
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) { | |||
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) { | |||
@@ -299,13 +318,25 @@ func PtracePokeText(pid int, addr uintptr, data []byte) (count int, 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) { | |||
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 | |||
*/ | |||
@@ -319,6 +350,7 @@ func PtraceSingleStep(pid int) (err error) { | |||
//sys Chmod(path string, mode uint32) (err error) | |||
//sys Chown(path string, uid int, gid int) (err error) | |||
//sys Chroot(path string) (err error) | |||
//sys ClockGettime(clockid int32, time *Timespec) (err error) | |||
//sys Close(fd int) (err error) | |||
//sys Dup(fd int) (nfd 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 Setresgid(rgid int, egid int, sgid 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 Settimeofday(tp *Timeval) (err error) | |||
//sysnb Setuid(uid int) (err error) | |||
@@ -42,6 +42,10 @@ func (cmsg *Cmsghdr) SetLen(length int) { | |||
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) { | |||
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) | |||
@@ -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 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) | |||
} | |||
func (d *PtraceIoDesc) SetLen(length int) { | |||
d.Len = uint64(length) | |||
} | |||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | |||
var writtenOut uint64 = 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 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) | |||
} | |||
func (d *PtraceIoDesc) SetLen(length int) { | |||
d.Len = uint32(length) | |||
} | |||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { | |||
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) | |||
@@ -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 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 | |||
} |