* update swagger to 0.20.1 * fiw swagger version for validatefor-closed-social
@ -1,15 +0,0 @@ | |||
# This is the official list of cloud authors for copyright purposes. | |||
# This file is distinct from the CONTRIBUTORS files. | |||
# See the latter for an explanation. | |||
# Names should be added to this file as: | |||
# Name or Organization <email address> | |||
# The email address is not required for organizations. | |||
Filippo Valsorda <hi@filippo.io> | |||
Google Inc. | |||
Ingo Oeser <nightlyone@googlemail.com> | |||
Palm Stone Games, Inc. | |||
Paweł Knap <pawelknap88@gmail.com> | |||
Péter Szilágyi <peterke@gmail.com> | |||
Tyler Treat <ttreat31@gmail.com> |
@ -1,40 +0,0 @@ | |||
# People who have agreed to one of the CLAs and can contribute patches. | |||
# The AUTHORS file lists the copyright holders; this file | |||
# lists people. For example, Google employees are listed here | |||
# but not in AUTHORS, because Google holds the copyright. | |||
# | |||
# https://developers.google.com/open-source/cla/individual | |||
# https://developers.google.com/open-source/cla/corporate | |||
# | |||
# Names should be added to this file as: | |||
# Name <email address> | |||
# Keep the list alphabetically sorted. | |||
Alexis Hunt <lexer@google.com> | |||
Andreas Litt <andreas.litt@gmail.com> | |||
Andrew Gerrand <adg@golang.org> | |||
Brad Fitzpatrick <bradfitz@golang.org> | |||
Burcu Dogan <jbd@google.com> | |||
Dave Day <djd@golang.org> | |||
David Sansome <me@davidsansome.com> | |||
David Symonds <dsymonds@golang.org> | |||
Filippo Valsorda <hi@filippo.io> | |||
Glenn Lewis <gmlewis@google.com> | |||
Ingo Oeser <nightlyone@googlemail.com> | |||
James Hall <james.hall@shopify.com> | |||
Johan Euphrosine <proppy@google.com> | |||
Jonathan Amsterdam <jba@google.com> | |||
Kunpei Sakai <namusyaka@gmail.com> | |||
Luna Duclos <luna.duclos@palmstonegames.com> | |||
Magnus Hiie <magnus.hiie@gmail.com> | |||
Mario Castro <mariocaster@gmail.com> | |||
Michael McGreevy <mcgreevy@golang.org> | |||
Omar Jarjur <ojarjur@google.com> | |||
Paweł Knap <pawelknap88@gmail.com> | |||
Péter Szilágyi <peterke@gmail.com> | |||
Sarah Adams <shadams@google.com> | |||
Thanatat Tamtan <acoshift@gmail.com> | |||
Toby Burress <kurin@google.com> | |||
Tuo Shan <shantuo@google.com> | |||
Tyler Treat <ttreat31@gmail.com> |
@ -0,0 +1 @@ | |||
module github.com/golang/snappy |
@ -0,0 +1,29 @@ | |||
// Copyright 2019 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. | |||
// +build go1.12 | |||
package prometheus | |||
import "runtime/debug" | |||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go 1.12+. | |||
func readBuildInfo() (path, version, sum string) { | |||
path, version, sum = "unknown", "unknown", "unknown" | |||
if bi, ok := debug.ReadBuildInfo(); ok { | |||
path = bi.Main.Path | |||
version = bi.Main.Version | |||
sum = bi.Main.Sum | |||
} | |||
return | |||
} |
@ -0,0 +1,22 @@ | |||
// Copyright 2019 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. | |||
// +build !go1.12 | |||
package prometheus | |||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go versions before | |||
// 1.12. Remove this whole file once the minimum supported Go version is 1.12. | |||
func readBuildInfo() (path, version, sum string) { | |||
return "unknown", "unknown", "unknown" | |||
} |
@ -1,505 +0,0 @@ | |||
// Copyright 2014 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 | |||
import ( | |||
"bufio" | |||
"compress/gzip" | |||
"io" | |||
"net" | |||
"net/http" | |||
"strconv" | |||
"strings" | |||
"sync" | |||
"time" | |||
"github.com/prometheus/common/expfmt" | |||
) | |||
// TODO(beorn7): Remove this whole file. It is a partial mirror of | |||
// promhttp/http.go (to avoid circular import chains) where everything HTTP | |||
// related should live. The functions here are just for avoiding | |||
// breakage. Everything is deprecated. | |||
const ( | |||
contentTypeHeader = "Content-Type" | |||
contentEncodingHeader = "Content-Encoding" | |||
acceptEncodingHeader = "Accept-Encoding" | |||
) | |||
var gzipPool = sync.Pool{ | |||
New: func() interface{} { | |||
return gzip.NewWriter(nil) | |||
}, | |||
} | |||
// Handler returns an HTTP handler for the DefaultGatherer. It is | |||
// already instrumented with InstrumentHandler (using "prometheus" as handler | |||
// name). | |||
// | |||
// Deprecated: Please note the issues described in the doc comment of | |||
// InstrumentHandler. You might want to consider using promhttp.Handler instead. | |||
func Handler() http.Handler { | |||
return InstrumentHandler("prometheus", UninstrumentedHandler()) | |||
} | |||
// UninstrumentedHandler returns an HTTP handler for the DefaultGatherer. | |||
// | |||
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{}) | |||
// instead. See there for further documentation. | |||
func UninstrumentedHandler() http.Handler { | |||
return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { | |||
mfs, err := DefaultGatherer.Gather() | |||
if err != nil { | |||
httpError(rsp, err) | |||
return | |||
} | |||
contentType := expfmt.Negotiate(req.Header) | |||
header := rsp.Header() | |||
header.Set(contentTypeHeader, string(contentType)) | |||
w := io.Writer(rsp) | |||
if gzipAccepted(req.Header) { | |||
header.Set(contentEncodingHeader, "gzip") | |||
gz := gzipPool.Get().(*gzip.Writer) | |||
defer gzipPool.Put(gz) | |||
gz.Reset(w) | |||
defer gz.Close() | |||
w = gz | |||
} | |||
enc := expfmt.NewEncoder(w, contentType) | |||
for _, mf := range mfs { | |||
if err := enc.Encode(mf); err != nil { | |||
httpError(rsp, err) | |||
return | |||
} | |||
} | |||
}) | |||
} | |||
var instLabels = []string{"method", "code"} | |||
type nower interface { | |||
Now() time.Time | |||
} | |||
type nowFunc func() time.Time | |||
func (n nowFunc) Now() time.Time { | |||
return n() | |||
} | |||
var now nower = nowFunc(func() time.Time { | |||
return time.Now() | |||
}) | |||
// InstrumentHandler wraps the given HTTP handler for instrumentation. It | |||
// registers four metric collectors (if not already done) and reports HTTP | |||
// metrics to the (newly or already) registered collectors: http_requests_total | |||
// (CounterVec), http_request_duration_microseconds (Summary), | |||
// http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each | |||
// has a constant label named "handler" with the provided handlerName as | |||
// value. http_requests_total is a metric vector partitioned by HTTP method | |||
// (label name "method") and HTTP status code (label name "code"). | |||
// | |||
// Deprecated: InstrumentHandler has several issues. Use the tooling provided in | |||
// package promhttp instead. The issues are the following: (1) It uses Summaries | |||
// rather than Histograms. Summaries are not useful if aggregation across | |||
// multiple instances is required. (2) It uses microseconds as unit, which is | |||
// deprecated and should be replaced by seconds. (3) The size of the request is | |||
// calculated in a separate goroutine. Since this calculator requires access to | |||
// the request header, it creates a race with any writes to the header performed | |||
// during request handling. httputil.ReverseProxy is a prominent example for a | |||
// handler performing such writes. (4) It has additional issues with HTTP/2, cf. | |||
// https://github.com/prometheus/client_golang/issues/272. | |||
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc { | |||
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP) | |||
} | |||
// InstrumentHandlerFunc wraps the given function for instrumentation. It | |||
// otherwise works in the same way as InstrumentHandler (and shares the same | |||
// issues). | |||
// | |||
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as | |||
// InstrumentHandler is. Use the tooling provided in package promhttp instead. | |||
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | |||
return InstrumentHandlerFuncWithOpts( | |||
SummaryOpts{ | |||
Subsystem: "http", | |||
ConstLabels: Labels{"handler": handlerName}, | |||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, | |||
}, | |||
handlerFunc, | |||
) | |||
} | |||
// InstrumentHandlerWithOpts works like InstrumentHandler (and shares the same | |||
// issues) but provides more flexibility (at the cost of a more complex call | |||
// syntax). As InstrumentHandler, this function registers four metric | |||
// collectors, but it uses the provided SummaryOpts to create them. However, the | |||
// fields "Name" and "Help" in the SummaryOpts are ignored. "Name" is replaced | |||
// by "requests_total", "request_duration_microseconds", "request_size_bytes", | |||
// and "response_size_bytes", respectively. "Help" is replaced by an appropriate | |||
// help string. The names of the variable labels of the http_requests_total | |||
// CounterVec are "method" (get, post, etc.), and "code" (HTTP status code). | |||
// | |||
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the | |||
// behavior of InstrumentHandler: | |||
// | |||
// prometheus.InstrumentHandlerWithOpts( | |||
// prometheus.SummaryOpts{ | |||
// Subsystem: "http", | |||
// ConstLabels: prometheus.Labels{"handler": handlerName}, | |||
// }, | |||
// handler, | |||
// ) | |||
// | |||
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it | |||
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally, | |||
// and all its fields are set to the equally named fields in the provided | |||
// SummaryOpts. | |||
// | |||
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as | |||
// InstrumentHandler is. Use the tooling provided in package promhttp instead. | |||
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc { | |||
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP) | |||
} | |||
// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc (and shares | |||
// the same issues) but provides more flexibility (at the cost of a more complex | |||
// call syntax). See InstrumentHandlerWithOpts for details how the provided | |||
// SummaryOpts are used. | |||
// | |||
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons | |||
// as InstrumentHandler is. Use the tooling provided in package promhttp instead. | |||
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | |||
reqCnt := NewCounterVec( | |||
CounterOpts{ | |||
Namespace: opts.Namespace, | |||
Subsystem: opts.Subsystem, | |||
Name: "requests_total", | |||
Help: "Total number of HTTP requests made.", | |||
ConstLabels: opts.ConstLabels, | |||
}, | |||
instLabels, | |||
) | |||
if err := Register(reqCnt); err != nil { | |||
if are, ok := err.(AlreadyRegisteredError); ok { | |||
reqCnt = are.ExistingCollector.(*CounterVec) | |||
} else { | |||
panic(err) | |||
} | |||
} | |||
opts.Name = "request_duration_microseconds" | |||
opts.Help = "The HTTP request latencies in microseconds." | |||
reqDur := NewSummary(opts) | |||
if err := Register(reqDur); err != nil { | |||
if are, ok := err.(AlreadyRegisteredError); ok { | |||
reqDur = are.ExistingCollector.(Summary) | |||
} else { | |||
panic(err) | |||
} | |||
} | |||
opts.Name = "request_size_bytes" | |||
opts.Help = "The HTTP request sizes in bytes." | |||
reqSz := NewSummary(opts) | |||
if err := Register(reqSz); err != nil { | |||
if are, ok := err.(AlreadyRegisteredError); ok { | |||
reqSz = are.ExistingCollector.(Summary) | |||
} else { | |||
panic(err) | |||
} | |||
} | |||
opts.Name = "response_size_bytes" | |||
opts.Help = "The HTTP response sizes in bytes." | |||
resSz := NewSummary(opts) | |||
if err := Register(resSz); err != nil { | |||
if are, ok := err.(AlreadyRegisteredError); ok { | |||
resSz = are.ExistingCollector.(Summary) | |||
} else { | |||
panic(err) | |||
} | |||
} | |||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
now := time.Now() | |||
delegate := &responseWriterDelegator{ResponseWriter: w} | |||
out := computeApproximateRequestSize(r) | |||
_, cn := w.(http.CloseNotifier) | |||
_, fl := w.(http.Flusher) | |||
_, hj := w.(http.Hijacker) | |||
_, rf := w.(io.ReaderFrom) | |||
var rw http.ResponseWriter | |||
if cn && fl && hj && rf { | |||
rw = &fancyResponseWriterDelegator{delegate} | |||
} else { | |||
rw = delegate | |||
} | |||
handlerFunc(rw, r) | |||
elapsed := float64(time.Since(now)) / float64(time.Microsecond) | |||
method := sanitizeMethod(r.Method) | |||
code := sanitizeCode(delegate.status) | |||
reqCnt.WithLabelValues(method, code).Inc() | |||
reqDur.Observe(elapsed) | |||
resSz.Observe(float64(delegate.written)) | |||
reqSz.Observe(float64(<-out)) | |||
}) | |||
} | |||
func computeApproximateRequestSize(r *http.Request) <-chan int { | |||
// Get URL length in current goroutine for avoiding a race condition. | |||
// HandlerFunc that runs in parallel may modify the URL. | |||
s := 0 | |||
if r.URL != nil { | |||
s += len(r.URL.String()) | |||
} | |||
out := make(chan int, 1) | |||
go func() { | |||
s += len(r.Method) | |||
s += len(r.Proto) | |||
for name, values := range r.Header { | |||
s += len(name) | |||
for _, value := range values { | |||
s += len(value) | |||
} | |||
} | |||
s += len(r.Host) | |||
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL. | |||
if r.ContentLength != -1 { | |||
s += int(r.ContentLength) | |||
} | |||
out <- s | |||
close(out) | |||
}() | |||
return out | |||
} | |||
type responseWriterDelegator struct { | |||
http.ResponseWriter | |||
status int | |||
written int64 | |||
wroteHeader bool | |||
} | |||
func (r *responseWriterDelegator) WriteHeader(code int) { | |||
r.status = code | |||
r.wroteHeader = true | |||
r.ResponseWriter.WriteHeader(code) | |||
} | |||
func (r *responseWriterDelegator) Write(b []byte) (int, error) { | |||
if !r.wroteHeader { | |||
r.WriteHeader(http.StatusOK) | |||
} | |||
n, err := r.ResponseWriter.Write(b) | |||
r.written += int64(n) | |||
return n, err | |||
} | |||
type fancyResponseWriterDelegator struct { | |||
*responseWriterDelegator | |||
} | |||
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool { | |||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to | |||
//remove support from client_golang yet. | |||
return f.ResponseWriter.(http.CloseNotifier).CloseNotify() | |||
} | |||
func (f *fancyResponseWriterDelegator) Flush() { | |||
f.ResponseWriter.(http.Flusher).Flush() | |||
} | |||
func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { | |||
return f.ResponseWriter.(http.Hijacker).Hijack() | |||
} | |||
func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) { | |||
if !f.wroteHeader { | |||
f.WriteHeader(http.StatusOK) | |||
} | |||
n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r) | |||
f.written += n | |||
return n, err | |||
} | |||
func sanitizeMethod(m string) string { | |||
switch m { | |||
case "GET", "get": | |||
return "get" | |||
case "PUT", "put": | |||
return "put" | |||
case "HEAD", "head": | |||
return "head" | |||
case "POST", "post": | |||
return "post" | |||
case "DELETE", "delete": | |||
return "delete" | |||
case "CONNECT", "connect": | |||
return "connect" | |||
case "OPTIONS", "options": | |||
return "options" | |||
case "NOTIFY", "notify": | |||
return "notify" | |||
default: | |||
return strings.ToLower(m) | |||
} | |||
} | |||
func sanitizeCode(s int) string { | |||
switch s { | |||
case 100: | |||
return "100" | |||
case 101: | |||
return "101" | |||
case 200: | |||
return "200" | |||
case 201: | |||
return "201" | |||
case 202: | |||
return "202" | |||
case 203: | |||
return "203" | |||
case 204: | |||
return "204" | |||
case 205: | |||
return "205" | |||
case 206: | |||
return "206" | |||
case 300: | |||
return "300" | |||
case 301: | |||
return "301" | |||
case 302: | |||
return "302" | |||
case 304: | |||
return "304" | |||
case 305: | |||
return "305" | |||
case 307: | |||
return "307" | |||
case 400: | |||
return "400" | |||
case 401: | |||
return "401" | |||
case 402: | |||
return "402" | |||
case 403: | |||
return "403" | |||
case 404: | |||
return "404" | |||
case 405: | |||
return "405" | |||
case 406: | |||
return "406" | |||
case 407: | |||
return "407" | |||
case 408: | |||
return "408" | |||
case 409: | |||
return "409" | |||
case 410: | |||
return "410" | |||
case 411: | |||
return "411" | |||
case 412: | |||
return "412" | |||
case 413: | |||
return "413" | |||
case 414: | |||
return "414" | |||
case 415: | |||
return "415" | |||
case 416: | |||
return "416" | |||
case 417: | |||
return "417" | |||
case 418: | |||
return "418" | |||
case 500: | |||
return "500" | |||
case 501: | |||
return "501" | |||
case 502: | |||
return "502" | |||
case 503: | |||
return "503" | |||
case 504: | |||
return "504" | |||
case 505: | |||
return "505" | |||
case 428: | |||
return "428" | |||
case 429: | |||
return "429" | |||
case 431: | |||
return "431" | |||
case 511: | |||
return "511" | |||
default: | |||
return strconv.Itoa(s) | |||
} | |||
} | |||
// gzipAccepted returns whether the client will accept gzip-encoded content. | |||
func gzipAccepted(header http.Header) bool { | |||
a := header.Get(acceptEncodingHeader) | |||
parts := strings.Split(a, ",") | |||
for _, part := range parts { | |||
part = strings.TrimSpace(part) | |||
if part == "gzip" || strings.HasPrefix(part, "gzip;") { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// httpError removes any content-encoding header and then calls http.Error with | |||
// the provided error and http.StatusInternalServerErrer. Error contents is | |||
// supposed to be uncompressed plain text. However, same as with a plain | |||
// http.Error, any header settings will be void if the header has already been | |||
// sent. The error message will still be written to the writer, but it will | |||
// probably be of limited use. | |||
func httpError(rsp http.ResponseWriter, err error) { | |||
rsp.Header().Del(contentEncodingHeader) | |||
http.Error( | |||
rsp, | |||
"An error has occurred while serving metrics:\n\n"+err.Error(), | |||
http.StatusInternalServerError, | |||
) | |||
} |
@ -0,0 +1,65 @@ | |||
// Copyright 2019 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. | |||
// +build !windows | |||
package prometheus | |||
import ( | |||
"github.com/prometheus/procfs" | |||
) | |||
func canCollectProcess() bool { | |||
_, err := procfs.NewDefaultFS() | |||
return err == nil | |||
} | |||
func (c *processCollector) processCollect(ch chan<- Metric) { | |||
pid, err := c.pidFn() | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
p, err := procfs.NewProc(pid) | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
if stat, err := p.Stat(); err == nil { | |||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime()) | |||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory())) | |||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory())) | |||
if startTime, err := stat.StartTime(); err == nil { | |||
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) | |||
} else { | |||
c.reportError(ch, c.startTime, err) | |||
} | |||
} else { | |||
c.reportError(ch, nil, err) | |||
} | |||
if fds, err := p.FileDescriptorsLen(); err == nil { | |||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds)) | |||
} else { | |||
c.reportError(ch, c.openFDs, err) | |||
} | |||
if limits, err := p.Limits(); err == nil { | |||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles)) | |||
ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace)) | |||
} else { | |||
c.reportError(ch, nil, err) | |||
} | |||
} |
@ -0,0 +1,112 @@ | |||
// Copyright 2019 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 | |||
import ( | |||
"syscall" | |||
"unsafe" | |||
"golang.org/x/sys/windows" | |||
) | |||
func canCollectProcess() bool { | |||
return true | |||
} | |||
var ( | |||
modpsapi = syscall.NewLazyDLL("psapi.dll") | |||
modkernel32 = syscall.NewLazyDLL("kernel32.dll") | |||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") | |||
procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") | |||
) | |||
type processMemoryCounters struct { | |||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-_process_memory_counters_ex | |||
_ uint32 | |||
PageFaultCount uint32 | |||
PeakWorkingSetSize uint64 | |||
WorkingSetSize uint64 | |||
QuotaPeakPagedPoolUsage uint64 | |||
QuotaPagedPoolUsage uint64 | |||
QuotaPeakNonPagedPoolUsage uint64 | |||
QuotaNonPagedPoolUsage uint64 | |||
PagefileUsage uint64 | |||
PeakPagefileUsage uint64 | |||
PrivateUsage uint64 | |||
} | |||
func getProcessMemoryInfo(handle windows.Handle) (processMemoryCounters, error) { | |||
mem := processMemoryCounters{} | |||
r1, _, err := procGetProcessMemoryInfo.Call( | |||
uintptr(handle), | |||
uintptr(unsafe.Pointer(&mem)), | |||
uintptr(unsafe.Sizeof(mem)), | |||
) | |||
if r1 != 1 { | |||
return mem, err | |||
} else { | |||
return mem, nil | |||
} | |||
} | |||
func getProcessHandleCount(handle windows.Handle) (uint32, error) { | |||
var count uint32 | |||
r1, _, err := procGetProcessHandleCount.Call( | |||
uintptr(handle), | |||
uintptr(unsafe.Pointer(&count)), | |||
) | |||
if r1 != 1 { | |||
return 0, err | |||
} else { | |||
return count, nil | |||
} | |||
} | |||
func (c *processCollector) processCollect(ch chan<- Metric) { | |||
h, err := windows.GetCurrentProcess() | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
var startTime, exitTime, kernelTime, userTime windows.Filetime | |||
err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
ch <- MustNewConstMetric(c.startTime, GaugeValue, float64(startTime.Nanoseconds()/1e9)) | |||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, fileTimeToSeconds(kernelTime)+fileTimeToSeconds(userTime)) | |||
mem, err := getProcessMemoryInfo(h) | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(mem.PrivateUsage)) | |||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(mem.WorkingSetSize)) | |||
handles, err := getProcessHandleCount(h) | |||
if err != nil { | |||
c.reportError(ch, nil, err) | |||
return | |||
} | |||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(handles)) | |||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process. | |||
} | |||
func fileTimeToSeconds(ft windows.Filetime) float64 { | |||
return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7 | |||
} |
@ -0,0 +1,85 @@ | |||
// Copyright 2019 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" | |||
"io/ioutil" | |||
"net" | |||
"strings" | |||
) | |||
// ARPEntry contains a single row of the columnar data represented in | |||
// /proc/net/arp. | |||
type ARPEntry struct { | |||
// IP address | |||
IPAddr net.IP | |||
// MAC address | |||
HWAddr net.HardwareAddr | |||
// Name of the device | |||
Device string | |||
} | |||
// GatherARPEntries retrieves all the ARP entries, parse the relevant columns, | |||
// and then return a slice of ARPEntry's. | |||
func (fs FS) GatherARPEntries() ([]ARPEntry, error) { | |||
data, err := ioutil.ReadFile(fs.proc.Path("net/arp")) | |||
if err != nil { | |||
return nil, fmt.Errorf("error reading arp %s: %s", fs.proc.Path("net/arp"), err) | |||
} | |||
return parseARPEntries(data) | |||
} | |||
func parseARPEntries(data []byte) ([]ARPEntry, error) { | |||
lines := strings.Split(string(data), "\n") | |||
entries := make([]ARPEntry, 0) | |||
var err error | |||
const ( | |||
expectedDataWidth = 6 | |||
expectedHeaderWidth = 9 | |||
) | |||
for _, line := range lines { | |||
columns := strings.Fields(line) | |||
width := len(columns) | |||
if width == expectedHeaderWidth || width == 0 { | |||
continue | |||
} else if width == expectedDataWidth { | |||
entry, err := parseARPEntry(columns) | |||
if err != nil { | |||
return []ARPEntry{}, fmt.Errorf("failed to parse ARP entry: %s", err) | |||
} | |||
entries = append(entries, entry) | |||
} else { | |||
return []ARPEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedDataWidth) | |||
} | |||
} | |||
return entries, err | |||
} | |||
func parseARPEntry(columns []string) (ARPEntry, error) { | |||
ip := net.ParseIP(columns[0]) | |||
mac := net.HardwareAddr(columns[3]) | |||
entry := ARPEntry{ | |||
IPAddr: ip, | |||
HWAddr: mac, | |||
Device: columns[5], | |||
} | |||
return entry, nil | |||
} |
@ -0,0 +1,131 @@ | |||
// Copyright 2019 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 ( | |||
"bytes" | |||
"fmt" | |||
"io/ioutil" | |||
"strconv" | |||
"strings" | |||
"github.com/prometheus/procfs/internal/util" | |||
) | |||
// Crypto holds info parsed from /proc/crypto. | |||
type Crypto struct { | |||
Alignmask *uint64 | |||
Async bool | |||
Blocksize *uint64 | |||
Chunksize *uint64 | |||
Ctxsize *uint64 | |||
Digestsize *uint64 | |||
Driver string | |||
Geniv string | |||
Internal string | |||
Ivsize *uint64 | |||
Maxauthsize *uint64 | |||
MaxKeysize *uint64 | |||
MinKeysize *uint64 | |||
Module string | |||
Name string | |||
Priority *int64 | |||
Refcnt *int64 | |||
Seedsize *uint64 | |||
Selftest string | |||
Type string | |||
Walksize *uint64 | |||
} | |||
// Crypto parses an crypto-file (/proc/crypto) and returns a slice of | |||
// structs containing the relevant info. More information available here: | |||
// https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html | |||
func (fs FS) Crypto() ([]Crypto, error) { | |||
data, err := ioutil.ReadFile(fs.proc.Path("crypto")) | |||
if err != nil { | |||
return nil, fmt.Errorf("error parsing crypto %s: %s", fs.proc.Path("crypto"), err) | |||
} | |||
crypto, err := parseCrypto(data) | |||
if err != nil { | |||
return nil, fmt.Errorf("error parsing crypto %s: %s", fs.proc.Path("crypto"), err) | |||
} | |||
return crypto, nil | |||
} | |||
func parseCrypto(cryptoData []byte) ([]Crypto, error) { | |||
crypto := []Crypto{} | |||
cryptoBlocks := bytes.Split(cryptoData, []byte("\n\n")) | |||
for _, block := range cryptoBlocks { | |||
var newCryptoElem Crypto | |||
lines := strings.Split(string(block), "\n") | |||
for _, line := range lines { | |||
if strings.TrimSpace(line) == "" || line[0] == ' ' { | |||
continue | |||
} | |||
fields := strings.Split(line, ":") | |||
key := strings.TrimSpace(fields[0]) | |||
value := strings.TrimSpace(fields[1]) | |||
vp := util.NewValueParser(value) | |||
switch strings.TrimSpace(key) { | |||
case "async": | |||
b, err := strconv.ParseBool(value) | |||
if err == nil { | |||
newCryptoElem.Async = b | |||
} | |||
case "blocksize": | |||
newCryptoElem.Blocksize = vp.PUInt64() | |||
case "chunksize": | |||
newCryptoElem.Chunksize = vp.PUInt64() | |||
case "digestsize": | |||
newCryptoElem.Digestsize = vp.PUInt64() | |||
case "driver": | |||
newCryptoElem.Driver = value | |||
case "geniv": | |||
newCryptoElem.Geniv = value | |||
case "internal": | |||
newCryptoElem.Internal = value | |||
case "ivsize": | |||
newCryptoElem.Ivsize = vp.PUInt64() | |||
case "maxauthsize": | |||
newCryptoElem.Maxauthsize = vp.PUInt64() | |||
case "max keysize": | |||
newCryptoElem.MaxKeysize = vp.PUInt64() | |||
case "min keysize": | |||
newCryptoElem.MinKeysize = vp.PUInt64() | |||
case "module": | |||
newCryptoElem.Module = value | |||
case "name": | |||
newCryptoElem.Name = value | |||
case "priority": | |||
newCryptoElem.Priority = vp.PInt64() | |||
case "refcnt": | |||
newCryptoElem.Refcnt = vp.PInt64() | |||
case "seedsize": | |||
newCryptoElem.Seedsize = vp.PUInt64() | |||
case "selftest": | |||
newCryptoElem.Selftest = value | |||
case "type": | |||
newCryptoElem.Type = value | |||
case "walksize": | |||
newCryptoElem.Walksize = vp.PUInt64() | |||
} | |||
} | |||
crypto = append(crypto, newCryptoElem) | |||
} | |||
return crypto, nil | |||
} |
@ -1,3 +1,6 @@ | |||
module github.com/prometheus/procfs | |||
require golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 | |||
require ( | |||
github.com/google/go-cmp v0.3.0 | |||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 | |||
) |
@ -1,2 +1,4 @@ | |||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= | |||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= | |||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
@ -0,0 +1,88 @@ | |||
// Copyright 2018 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 util | |||
import ( | |||
"io/ioutil" | |||
"strconv" | |||
"strings" | |||
) | |||
// ParseUint32s parses a slice of strings into a slice of uint32s. | |||
func ParseUint32s(ss []string) ([]uint32, error) { | |||
us := make([]uint32, 0, len(ss)) | |||
for _, s := range ss { | |||
u, err := strconv.ParseUint(s, 10, 32) | |||
if err != nil { | |||
return nil, err | |||
} | |||
us = append(us, uint32(u)) | |||
} | |||
return us, nil | |||
} | |||
// ParseUint64s parses a slice of strings into a slice of uint64s. | |||
func ParseUint64s(ss []string) ([]uint64, error) { | |||
us := make([]uint64, 0, len(ss)) | |||
for _, s := range ss { | |||
u, err := strconv.ParseUint(s, 10, 64) | |||
if err != nil { | |||
return nil, err | |||
} | |||
us = append(us, u) | |||
} | |||
return us, nil | |||
} | |||
// ParsePInt64s parses a slice of strings into a slice of int64 pointers. | |||
func ParsePInt64s(ss []string) ([]*int64, error) { | |||
us := make([]*int64, 0, len(ss)) | |||
for _, s := range ss { | |||
u, err := strconv.ParseInt(s, 10, 64) | |||
if err != nil { | |||
return nil, err | |||
} | |||
us = append(us, &u) | |||
} | |||
return us, nil | |||
} | |||
// ReadUintFromFile reads a file and attempts to parse a uint64 from it. | |||
func ReadUintFromFile(path string) (uint64, error) { | |||
data, err := ioutil.ReadFile(path) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) | |||
} | |||
// ParseBool parses a string into a boolean pointer. | |||
func ParseBool(b string) *bool { | |||
var truth bool | |||
switch b { | |||
case "enabled": | |||
truth = true | |||
case "disabled": | |||
truth = false | |||
default: | |||
return nil | |||
} | |||
return &truth | |||
} |
@ -0,0 +1,45 @@ | |||
// Copyright 2018 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. | |||
// +build linux,!appengine | |||
package util | |||
import ( | |||
"bytes" | |||
"os" | |||
"syscall" | |||
) | |||
// SysReadFile is a simplified ioutil.ReadFile that invokes syscall.Read directly. | |||
// https://github.com/prometheus/node_exporter/pull/728/files | |||
func SysReadFile(file string) (string, error) { | |||
f, err := os.Open(file) | |||
if err != nil { | |||
return "", err | |||
} | |||
defer f.Close() | |||
// On some machines, hwmon drivers are broken and return EAGAIN. This causes | |||
// Go's ioutil.ReadFile implementation to poll forever. | |||
// | |||
// Since we either want to read data or bail immediately, do the simplest | |||
// possible read using syscall directly. | |||
b := make([]byte, 128) | |||
n, err := syscall.Read(int(f.Fd()), b) | |||
if err != nil { | |||
return "", err | |||
} | |||
return string(bytes.TrimSpace(b[:n])), nil | |||
} |
@ -0,0 +1,26 @@ | |||
// Copyright 2019 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. | |||
// +build linux,appengine !linux | |||
package util | |||
import ( | |||
"fmt" | |||
) | |||
// SysReadFile is here implemented as a noop for builds that do not support | |||
// the read syscall. For example Windows, or Linux on Google App Engine. | |||
func SysReadFile(file string) (string, error) { | |||
return "", fmt.Errorf("not supported on this platform") | |||
} |
@ -0,0 +1,77 @@ | |||
// Copyright 2019 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 util | |||
import ( | |||
"strconv" | |||
) | |||
// TODO(mdlayher): util packages are an anti-pattern and this should be moved | |||
// somewhere else that is more focused in the future. | |||
// A ValueParser enables parsing a single string into a variety of data types | |||
// in a concise and safe way. The Err method must be invoked after invoking | |||
// any other methods to ensure a value was successfully parsed. | |||
type ValueParser struct { | |||
v string | |||
err error | |||
} | |||
// NewValueParser creates a ValueParser using the input string. | |||
func NewValueParser(v string) *ValueParser { | |||
return &ValueParser{v: v} | |||
} | |||
// PInt64 interprets the underlying value as an int64 and returns a pointer to | |||
// that value. | |||
func (vp *ValueParser) PInt64() *int64 { | |||
if vp.err != nil { | |||
return nil | |||
} | |||
// A base value of zero makes ParseInt infer the correct base using the | |||
// string's prefix, if any. | |||
const base = 0 | |||
v, err := strconv.ParseInt(vp.v, base, 64) | |||
if err != nil { | |||
vp.err = err | |||
return nil | |||
} | |||
return &v | |||
} | |||
// PUInt64 interprets the underlying value as an uint64 and returns a pointer to | |||
// that value. | |||
func (vp *ValueParser) PUInt64() *uint64 { | |||
if vp.err != nil { | |||
return nil | |||
} | |||
// A base value of zero makes ParseInt infer the correct base using the | |||
// string's prefix, if any. | |||
const base = 0 | |||
v, err := strconv.ParseUint(vp.v, base, 64) | |||
if err != nil { | |||
vp.err = err | |||
return nil | |||
} | |||
return &v | |||
} | |||
// Err returns the last error, if any, encountered by the ValueParser. | |||
func (vp *ValueParser) Err() error { | |||
return vp.err | |||
} |
@ -0,0 +1,178 @@ | |||
// Copyright 2019 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" | |||
"fmt" | |||
"io" | |||
"os" | |||
"strconv" | |||
"strings" | |||
) | |||
var validOptionalFields = map[string]bool{ | |||
"shared": true, | |||
"master": true, | |||
"propagate_from": true, | |||
"unbindable": true, | |||
} | |||
// A MountInfo is a type that describes the details, options | |||
// for each mount, parsed from /proc/self/mountinfo. | |||
// The fields described in each entry of /proc/self/mountinfo | |||
// is described in the following man page. | |||
// http://man7.org/linux/man-pages/man5/proc.5.html | |||
type MountInfo struct { | |||
// Unique Id for the mount | |||
MountId int | |||
// The Id of the parent mount | |||
ParentId int | |||
// The value of `st_dev` for the files on this FS | |||
MajorMinorVer string | |||
// The pathname of the directory in the FS that forms | |||
// the root for this mount | |||
Root string | |||
// The pathname of the mount point relative to the root | |||
MountPoint string | |||
// Mount options | |||
Options map[string]string | |||
// Zero or more optional fields | |||
OptionalFields map[string]string | |||
// The Filesystem type | |||
FSType string | |||
// FS specific information or "none" | |||
Source string | |||
// Superblock options | |||
SuperOptions map[string]string | |||
} | |||
// Returns part of the mountinfo line, if it exists, else an empty string. | |||
func getStringSliceElement(parts []string, idx int, defaultValue string) string { | |||
if idx >= len(parts) { | |||
return defaultValue | |||
} | |||
return parts[idx] | |||
} | |||
// Reads each line of the mountinfo file, and returns a list of formatted MountInfo structs. | |||
func parseMountInfo(r io.Reader) ([]*MountInfo, error) { | |||
mounts := []*MountInfo{} | |||
scanner := bufio.NewScanner(r) | |||
for scanner.Scan() { | |||
mountString := scanner.Text() | |||
parsedMounts, err := parseMountInfoString(mountString) | |||
if err != nil { | |||
return nil, err | |||
} | |||
mounts = append(mounts, parsedMounts) | |||
} | |||
err := scanner.Err() | |||
return mounts, err | |||
} | |||
// Parses a mountinfo file line, and converts it to a MountInfo struct. | |||
// An important check here is to see if the hyphen separator, as if it does not exist, | |||
// it means that the line is malformed. | |||
func parseMountInfoString(mountString string) (*MountInfo, error) { | |||
var err error | |||
// OptionalFields can be zero, hence these checks to ensure we do not populate the wrong values in the wrong spots | |||
separatorIndex := strings.Index(mountString, "-") | |||
if separatorIndex == -1 { | |||
return nil, fmt.Errorf("no separator found in mountinfo string: %s", mountString) | |||
} | |||
beforeFields := strings.Fields(mountString[:separatorIndex]) | |||
afterFields := strings.Fields(mountString[separatorIndex+1:]) | |||
if (len(beforeFields) + len(afterFields)) < 7 { | |||
return nil, fmt.Errorf("too few fields") | |||
} | |||
mount := &MountInfo{ | |||
MajorMinorVer: getStringSliceElement(beforeFields, 2, ""), | |||
Root: getStringSliceElement(beforeFields, 3, ""), | |||
MountPoint: getStringSliceElement(beforeFields, 4, ""), | |||
Options: mountOptionsParser(getStringSliceElement(beforeFields, 5, "")), | |||
OptionalFields: nil, | |||
FSType: getStringSliceElement(afterFields, 0, ""), | |||
Source: getStringSliceElement(afterFields, 1, ""), | |||
SuperOptions: mountOptionsParser(getStringSliceElement(afterFields, 2, "")), | |||
} | |||
mount.MountId, err = strconv.Atoi(getStringSliceElement(beforeFields, 0, "")) | |||
if err != nil { | |||
return nil, fmt.Errorf("failed to parse mount ID") | |||
} | |||
mount.ParentId, err = strconv.Atoi(getStringSliceElement(beforeFields, 1, "")) | |||
if err != nil { | |||
return nil, fmt.Errorf("failed to parse parent ID") | |||
} | |||
// Has optional fields, which is a space separated list of values. | |||
// Example: shared:2 master:7 | |||
if len(beforeFields) > 6 { | |||
mount.OptionalFields = make(map[string]string) | |||
optionalFields := beforeFields[6:] | |||
for _, field := range optionalFields { | |||
optionSplit := strings.Split(field, ":") | |||
target, value := optionSplit[0], "" | |||
if len(optionSplit) == 2 { | |||
value = optionSplit[1] | |||
} | |||
// Checks if the 'keys' in the optional fields in the mountinfo line are acceptable. | |||
// Allowed 'keys' are shared, master, propagate_from, unbindable. | |||
if _, ok := validOptionalFields[target]; ok { | |||
mount.OptionalFields[target] = value | |||
} | |||
} | |||
} | |||
return mount, nil | |||
} | |||
// Parses the mount options, superblock options. | |||
func mountOptionsParser(mountOptions string) map[string]string { | |||
opts := make(map[string]string) | |||
options := strings.Split(mountOptions, ",") | |||
for _, opt := range options { | |||
splitOption := strings.Split(opt, "=") | |||
if len(splitOption) < 2 { | |||
key := splitOption[0] | |||
opts[key] = "" | |||
} else { | |||
key, value := splitOption[0], splitOption[1] | |||
opts[key] = value | |||
} | |||
} | |||
return opts | |||
} | |||
// Retrieves mountinfo information from `/proc/self/mountinfo`. | |||
func GetMounts() ([]*MountInfo, error) { | |||
f, err := os.Open("/proc/self/mountinfo") | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer f.Close() | |||
return parseMountInfo(f) | |||
} | |||
// Retrieves mountinfo information from a processes' `/proc/<pid>/mountinfo`. | |||
func GetProcMounts(pid int) ([]*MountInfo, error) { | |||
f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer f.Close() | |||
return parseMountInfo(f) | |||
} |
@ -0,0 +1,91 @@ | |||
// Copyright 2019 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" | |||
"io/ioutil" | |||
"strconv" | |||
"strings" | |||
) | |||
// For the proc file format details, | |||
// see 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. | |||
// SoftnetEntry contains a single row of data from /proc/net/softnet_stat | |||
type SoftnetEntry struct { | |||
// Number of processed packets | |||
Processed uint | |||
// Number of dropped packets | |||
Dropped uint | |||
// Number of times processing packets ran out of quota | |||
TimeSqueezed uint | |||
} | |||
// GatherSoftnetStats reads /proc/net/softnet_stat, parse the relevant columns, | |||
// and then return a slice of SoftnetEntry's. | |||
func (fs FS) GatherSoftnetStats() ([]SoftnetEntry, error) { | |||
data, err := ioutil.ReadFile(fs.proc.Path("net/softnet_stat")) | |||
if err != nil { | |||
return nil, fmt.Errorf("error reading softnet %s: %s", fs.proc.Path("net/softnet_stat"), err) | |||
} | |||
return parseSoftnetEntries(data) | |||
} | |||
func parseSoftnetEntries(data []byte) ([]SoftnetEntry, error) { | |||
lines := strings.Split(string(data), "\n") | |||
entries := make([]SoftnetEntry, 0) | |||
var err error | |||
const ( | |||
expectedColumns = 11 | |||
) | |||
for _, line := range lines { | |||
columns := strings.Fields(line) | |||
width := len(columns) | |||
if width == 0 { | |||
continue | |||
} | |||
if width != expectedColumns { | |||
return []SoftnetEntry{}, fmt.Errorf("%d columns were detected, but %d were expected", width, expectedColumns) | |||
} | |||
var entry SoftnetEntry | |||
if entry, err = parseSoftnetEntry(columns); err != nil { | |||
return []SoftnetEntry{}, err | |||
} | |||
entries = append(entries, entry) | |||
} | |||
return entries, nil | |||
} | |||
func parseSoftnetEntry(columns []string) (SoftnetEntry, error) { | |||
var err error | |||
var processed, dropped, timeSqueezed uint64 | |||
if processed, err = strconv.ParseUint(columns[0], 16, 32); err != nil { | |||
return SoftnetEntry{}, fmt.Errorf("Unable to parse column 0: %s", err) | |||
} | |||
if dropped, err = strconv.ParseUint(columns[1], 16, 32); err != nil { | |||
return SoftnetEntry{}, fmt.Errorf("Unable to parse column 1: %s", err) | |||
} | |||
if timeSqueezed, err = strconv.ParseUint(columns[2], 16, 32); err != nil { | |||
return SoftnetEntry{}, fmt.Errorf("Unable to parse column 2: %s", err) | |||
} | |||
return SoftnetEntry{ | |||
Processed: uint(processed), | |||
Dropped: uint(dropped), | |||
TimeSqueezed: uint(timeSqueezed), | |||
}, nil | |||
} |
@ -0,0 +1,275 @@ | |||
// Copyright 2018 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" | |||
"errors" | |||
"fmt" | |||
"io" | |||
"os" | |||
"strconv" | |||
"strings" | |||
) | |||
// For the proc file format details, | |||
// see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815 | |||
// and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48. | |||
const ( | |||
netUnixKernelPtrIdx = iota | |||
netUnixRefCountIdx | |||
_ | |||
netUnixFlagsIdx | |||
netUnixTypeIdx | |||
netUnixStateIdx | |||
netUnixInodeIdx | |||
// Inode and Path are optional. | |||
netUnixStaticFieldsCnt = 6 | |||
) | |||
const ( | |||
netUnixTypeStream = 1 | |||
netUnixTypeDgram = 2 | |||
netUnixTypeSeqpacket = 5 | |||
netUnixFlagListen = 1 << 16 | |||
netUnixStateUnconnected = 1 | |||
netUnixStateConnecting = 2 | |||
netUnixStateConnected = 3 | |||
netUnixStateDisconnected = 4 | |||
) | |||
var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format") | |||
// NetUnixType is the type of the type field. | |||
type NetUnixType uint64 | |||
// NetUnixFlags is the type of the flags field. | |||
type NetUnixFlags uint64 | |||
// NetUnixState is the type of the state field. | |||
type NetUnixState uint64 | |||
// NetUnixLine represents a line of /proc/net/unix. | |||
type NetUnixLine struct { | |||
KernelPtr string | |||
RefCount uint64 | |||
Protocol uint64 | |||
Flags NetUnixFlags | |||
Type NetUnixType | |||
State NetUnixState | |||
Inode uint64 | |||
Path string | |||
} | |||
// NetUnix holds the data read from /proc/net/unix. | |||
type NetUnix struct { | |||
Rows []*NetUnixLine | |||
} | |||
// NewNetUnix returns data read from /proc/net/unix. | |||
func NewNetUnix() (*NetUnix, error) { | |||
fs, err := NewFS(DefaultMountPoint) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return fs.NewNetUnix() | |||
} | |||
// NewNetUnix returns data read from /proc/net/unix. | |||
func (fs FS) NewNetUnix() (*NetUnix, error) { | |||
return NewNetUnixByPath(fs.proc.Path("net/unix")) | |||
} | |||
// NewNetUnixByPath returns data read from /proc/net/unix by file path. | |||
// It might returns an error with partial parsed data, if an error occur after some data parsed. | |||
func NewNetUnixByPath(path string) (*NetUnix, error) { | |||
f, err := os.Open(path) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer f.Close() | |||
return NewNetUnixByReader(f) | |||
} | |||
// NewNetUnixByReader returns data read from /proc/net/unix by a reader. | |||
// It might returns an error with partial parsed data, if an error occur after some data parsed. | |||
func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) { | |||
nu := &NetUnix{ | |||
Rows: make([]*NetUnixLine, 0, 32), | |||
} | |||
scanner := bufio.NewScanner(reader) | |||
// Omit the header line. | |||
scanner.Scan() | |||
header := scanner.Text() | |||
// From the man page of proc(5), it does not contain an Inode field, | |||
// but in actually it exists. | |||
// This code works for both cases. | |||
hasInode := strings.Contains(header, "Inode") | |||
minFieldsCnt := netUnixStaticFieldsCnt | |||
if hasInode { | |||
minFieldsCnt++ | |||
} | |||
for scanner.Scan() { | |||
line := scanner.Text() | |||
item, err := nu.parseLine(line, hasInode, minFieldsCnt) | |||
if err != nil { | |||
return nu, err | |||
} | |||
nu.Rows = append(nu.Rows, item) | |||
} | |||
return nu, scanner.Err() | |||
} | |||
func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) { | |||
fields := strings.Fields(line) | |||
fieldsLen := len(fields) | |||
if fieldsLen < minFieldsCnt { | |||
return nil, fmt.Errorf( | |||
"Parse Unix domain failed: expect at least %d fields but got %d", | |||
minFieldsCnt, fieldsLen) | |||
} | |||
kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx]) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err) | |||
} | |||
users, err := u.parseUsers(fields[netUnixRefCountIdx]) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err) | |||
} | |||
flags, err := u.parseFlags(fields[netUnixFlagsIdx]) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err) | |||
} | |||
typ, err := u.parseType(fields[netUnixTypeIdx]) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err) | |||
} | |||
state, err := u.parseState(fields[netUnixStateIdx]) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err) | |||
} | |||
var inode uint64 | |||
if hasInode { | |||
inodeStr := fields[netUnixInodeIdx] | |||
inode, err = u.parseInode(inodeStr) | |||
if err != nil { | |||
return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err) | |||
} | |||
} | |||
nuLine := &NetUnixLine{ | |||
KernelPtr: kernelPtr, | |||
RefCount: users, | |||
Type: typ, | |||
Flags: flags, | |||
State: state, | |||
Inode: inode, | |||
} | |||
// Path field is optional. | |||
if fieldsLen > minFieldsCnt { | |||
pathIdx := netUnixInodeIdx + 1 | |||
if !hasInode { | |||
pathIdx-- | |||
} | |||
nuLine.Path = fields[pathIdx] | |||
} | |||
return nuLine, nil | |||
} | |||
func (u NetUnix) parseKernelPtr(str string) (string, error) { | |||
if !strings.HasSuffix(str, ":") { | |||
return "", errInvalidKernelPtrFmt | |||
} | |||
return str[:len(str)-1], nil | |||
} | |||
func (u NetUnix) parseUsers(hexStr string) (uint64, error) { | |||
return strconv.ParseUint(hexStr, 16, 32) | |||
} | |||
func (u NetUnix) parseProtocol(hexStr string) (uint64, error) { | |||
return strconv.ParseUint(hexStr, 16, 32) | |||
} | |||
func (u NetUnix) parseType(hexStr string) (NetUnixType, error) { | |||
typ, err := strconv.ParseUint(hexStr, 16, 16) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return NetUnixType(typ), nil | |||
} | |||
func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) { | |||
flags, err := strconv.ParseUint(hexStr, 16, 32) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return NetUnixFlags(flags), nil | |||
} | |||
func (u NetUnix) parseState(hexStr string) (NetUnixState, error) { | |||
st, err := strconv.ParseInt(hexStr, 16, 8) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return NetUnixState(st), nil | |||
} | |||
func (u NetUnix) parseInode(inodeStr string) (uint64, error) { | |||
return strconv.ParseUint(inodeStr, 10, 64) | |||
} | |||
func (t NetUnixType) String() string { | |||
switch t { | |||
case netUnixTypeStream: | |||
return "stream" | |||
case netUnixTypeDgram: | |||
return "dgram" | |||
case netUnixTypeSeqpacket: | |||
return "seqpacket" | |||
} | |||
return "unknown" | |||
} | |||
func (f NetUnixFlags) String() string { | |||
switch f { | |||
case netUnixFlagListen: | |||
return "listen" | |||
default: | |||
return "default" | |||
} | |||
} | |||
func (s NetUnixState) String() string { | |||
switch s { | |||
case netUnixStateUnconnected: | |||
return "unconnected" | |||
case netUnixStateConnecting: | |||
return "connecting" | |||
case netUnixStateConnected: | |||
return "connected" | |||
case netUnixStateDisconnected: | |||
return "disconnected" | |||
} | |||
return "unknown" | |||
} |
@ -0,0 +1,43 @@ | |||
// Copyright 2019 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 ( | |||
"io/ioutil" | |||
"os" | |||
"strings" | |||
) | |||
// Environ reads process environments from /proc/<pid>/environ | |||
func (p Proc) Environ() ([]string, error) { | |||
environments := make([]string, 0) | |||
f, err := os.Open(p.path("environ")) | |||
if err != nil { | |||
return environments, err | |||
} | |||
defer f.Close() | |||
data, err := ioutil.ReadAll(f) | |||
if err != nil { | |||
return environments, err | |||
} | |||
environments = strings.Split(string(data), "\000") | |||
if len(environments) > 0 { | |||
environments = environments[:len(environments)-1] | |||
} | |||
return environments, nil | |||
} |
@ -0,0 +1,132 @@ | |||
// Copyright 2019 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" | |||
"fmt" | |||
"io/ioutil" | |||
"os" | |||
"regexp" | |||
"strings" | |||
) | |||
// Regexp variables | |||
var ( | |||
rPos = regexp.MustCompile(`^pos:\s+(\d+)$`) | |||
rFlags = regexp.MustCompile(`^flags:\s+(\d+)$`) | |||
rMntID = regexp.MustCompile(`^mnt_id:\s+(\d+)$`) | |||
rInotify = regexp.MustCompile(`^inotify`) | |||
) | |||
// ProcFDInfo contains represents file descriptor information. | |||
type ProcFDInfo struct { | |||
// File descriptor | |||
FD string | |||
// File offset | |||
Pos string | |||
// File access mode and status flags | |||
Flags string | |||
// Mount point ID | |||
MntID string | |||
// List of inotify lines (structed) in the fdinfo file (kernel 3.8+ only) | |||
InotifyInfos []InotifyInfo | |||
} | |||
// FDInfo constructor. On kernels older than 3.8, InotifyInfos will always be empty. | |||
func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { | |||
f, err := os.Open(p.path("fdinfo", fd)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer f.Close() | |||
fdinfo, err := ioutil.ReadAll(f) | |||
if err != nil { | |||
return nil, fmt.Errorf("could not read %s: %s", f.Name(), err) | |||
} | |||
var text, pos, flags, mntid string | |||
var inotify []InotifyInfo | |||
scanner := bufio.NewScanner(strings.NewReader(string(fdinfo))) | |||
for scanner.Scan() { | |||
text = scanner.Text() | |||
if rPos.MatchString(text) { | |||
pos = rPos.FindStringSubmatch(text)[1] | |||
} else if rFlags.MatchString(text) { | |||
flags = rFlags.FindStringSubmatch(text)[1] | |||
} else if rMntID.MatchString(text) { | |||
mntid = rMntID.FindStringSubmatch(text)[1] | |||
} else if rInotify.MatchString(text) { | |||
newInotify, err := parseInotifyInfo(text) | |||
if err != nil { | |||
return nil, err | |||
} | |||
inotify = append(inotify, *newInotify) | |||
} | |||
} | |||
i := &ProcFDInfo{ | |||
FD: fd, | |||
Pos: pos, | |||
Flags: flags, | |||
MntID: mntid, | |||
InotifyInfos: inotify, | |||
} | |||
return i, nil | |||
} | |||
// InotifyInfo represents a single inotify line in the fdinfo file. | |||
type InotifyInfo struct { | |||
// Watch descriptor number | |||
WD string | |||
// Inode number | |||
Ino string | |||
// Device ID | |||
Sdev string | |||
// Mask of events being monitored | |||
Mask string | |||
} | |||
// InotifyInfo constructor. Only available on kernel 3.8+. | |||
func parseInotifyInfo(line string) (*InotifyInfo, error) { | |||
r := regexp.MustCompile(`^inotify\s+wd:([0-9a-f]+)\s+ino:([0-9a-f]+)\s+sdev:([0-9a-f]+)\s+mask:([0-9a-f]+)`) | |||
m := r.FindStringSubmatch(line) | |||
i := &InotifyInfo{ | |||
WD: m[1], | |||
Ino: m[2], | |||
Sdev: m[3], | |||
Mask: m[4], | |||
} | |||
return i, nil | |||
} | |||
// ProcFDInfos represents a list of ProcFDInfo structs. | |||
type ProcFDInfos []ProcFDInfo | |||
func (p ProcFDInfos) Len() int { return len(p) } | |||
func (p ProcFDInfos) Swap(i, j int) { p[i], p[j] = p[j], p[i] } | |||
func (p ProcFDInfos) Less(i, j int) bool { return p[i].FD < p[j].FD } | |||
// InotifyWatchLen returns the total number of inotify watches | |||
func (p ProcFDInfos) InotifyWatchLen() (int, error) { | |||
length := 0 | |||
for _, f := range p { | |||
length += len(f.InotifyInfos) | |||
} | |||
return length, nil | |||
} |
@ -0,0 +1,162 @@ | |||
// Copyright 2018 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 ( | |||
"bytes" | |||
"io/ioutil" | |||
"os" | |||
"strconv" | |||
"strings" | |||
) | |||
// ProcStat provides status information about the process, | |||
// read from /proc/[pid]/stat. | |||
type ProcStatus struct { | |||
// The process ID. | |||
PID int | |||
// The process name. | |||
Name string | |||
// Peak virtual memory size. | |||
VmPeak uint64 | |||
// Virtual memory size. | |||
VmSize uint64 | |||
// Locked memory size. | |||
VmLck uint64 | |||
// Pinned memory size. | |||
VmPin uint64 | |||
// Peak resident set size. | |||
VmHWM uint64 | |||
// Resident set size (sum of RssAnnon RssFile and RssShmem). | |||
VmRSS uint64 | |||
// Size of resident anonymous memory. | |||
RssAnon uint64 | |||
// Size of resident file mappings. | |||
RssFile uint64 | |||
// Size of resident shared memory. | |||
RssShmem uint64 | |||
// Size of data segments. | |||
VmData uint64 | |||
// Size of stack segments. | |||
VmStk uint64 | |||
// Size of text segments. | |||
VmExe uint64 | |||
// Shared library code size. | |||
VmLib uint64 | |||
// Page table entries size. | |||
VmPTE uint64 | |||
// Size of second-level page tables. | |||
VmPMD uint64 | |||
// Swapped-out virtual memory size by anonymous private. | |||
VmSwap uint64 | |||
// Size of hugetlb memory portions | |||
HugetlbPages uint64 | |||
// Number of voluntary context switches. | |||
VoluntaryCtxtSwitches uint64 | |||
// Number of involuntary context switches. | |||
NonVoluntaryCtxtSwitches uint64 | |||
} | |||
// NewStatus returns the current status information of the process. | |||
func (p Proc) NewStatus() (ProcStatus, error) { | |||
f, err := os.Open(p.path("status")) | |||
if err != nil { | |||
return ProcStatus{}, err | |||
} | |||
defer f.Close() | |||
data, err := ioutil.ReadAll(f) | |||
if err != nil { | |||
return ProcStatus{}, err | |||
} | |||
s := ProcStatus{PID: p.PID} | |||
lines := strings.Split(string(data), "\n") | |||
for _, line := range lines { | |||
if !bytes.Contains([]byte(line), []byte(":")) { | |||
continue | |||
} | |||
kv := strings.SplitN(line, ":", 2) | |||
// removes spaces | |||
k := string(strings.TrimSpace(kv[0])) | |||
v := string(strings.TrimSpace(kv[1])) | |||
// removes "kB" | |||
v = string(bytes.Trim([]byte(v), " kB")) | |||
// value to int when possible | |||
// we can skip error check here, 'cause vKBytes is not used when value is a string | |||
vKBytes, _ := strconv.ParseUint(v, 10, 64) | |||
// convert kB to B | |||
vBytes := vKBytes * 1024 | |||
s.fillStatus(k, v, vKBytes, vBytes) | |||
} | |||
return s, nil | |||
} | |||
func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintBytes uint64) { | |||
switch k { | |||
case "Name": | |||
s.Name = vString | |||
case "VmPeak": | |||
s.VmPeak = vUintBytes | |||
case "VmSize": | |||
s.VmSize = vUintBytes | |||
case "VmLck": | |||
s.VmLck = vUintBytes | |||
case "VmPin": | |||
s.VmPin = vUintBytes | |||
case "VmHWM": | |||
s.VmHWM = vUintBytes | |||
case "VmRSS": | |||
s.VmRSS = vUintBytes | |||
case "RssAnon": | |||
s.RssAnon = vUintBytes | |||
case "RssFile": | |||
s.RssFile = vUintBytes | |||
case "RssShmem": | |||
s.RssShmem = vUintBytes | |||
case "VmData": | |||
s.VmData = vUintBytes | |||
case "VmStk": | |||
s.VmStk = vUintBytes | |||
case "VmExe": | |||
s.VmExe = vUintBytes | |||
case "VmLib": | |||
s.VmLib = vUintBytes | |||
case "VmPTE": | |||
s.VmPTE = vUintBytes | |||
case "VmPMD": | |||
s.VmPMD = vUintBytes | |||
case "VmSwap": | |||
s.VmSwap = vUintBytes | |||
case "HugetlbPages": | |||
s.HugetlbPages = vUintBytes | |||
case "voluntary_ctxt_switches": | |||
s.VoluntaryCtxtSwitches = vUint | |||
case "nonvoluntary_ctxt_switches": | |||
s.NonVoluntaryCtxtSwitches = vUint | |||
} | |||
} | |||
// TotalCtxtSwitches returns the total context switch. | |||
func (s ProcStatus) TotalCtxtSwitches() uint64 { | |||
return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches | |||
} |
@ -0,0 +1,118 @@ | |||
// Copyright 2019 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" | |||
"errors" | |||
"os" | |||
"regexp" | |||
"strconv" | |||
) | |||
var ( | |||
cpuLineRE = regexp.MustCompile(`cpu(\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+) (\d+)`) | |||
procLineRE = regexp.MustCompile(`(\d+) (\d+) (\d+)`) | |||
) | |||
// Schedstat contains scheduler statistics from /proc/schedstats | |||
// | |||
// See | |||
// https://www.kernel.org/doc/Documentation/scheduler/sched-stats.txt | |||
// for a detailed description of what these numbers mean. | |||
// | |||
// Note the current kernel documentation claims some of the time units are in | |||
// jiffies when they are actually in nanoseconds since 2.6.23 with the | |||
// introduction of CFS. A fix to the documentation is pending. See | |||
// https://lore.kernel.org/patchwork/project/lkml/list/?series=403473 | |||
type Schedstat struct { | |||
CPUs []*SchedstatCPU | |||
} | |||
// SchedstatCPU contains the values from one "cpu<N>" line | |||
type SchedstatCPU struct { | |||
CPUNum string | |||
RunningNanoseconds uint64 | |||
WaitingNanoseconds uint64 | |||
RunTimeslices uint64 | |||
} | |||
// ProcSchedstat contains the values from /proc/<pid>/schedstat | |||
type ProcSchedstat struct { | |||
RunningNanoseconds uint64 | |||
WaitingNanoseconds uint64 | |||
RunTimeslices uint64 | |||
} | |||
func (fs FS) Schedstat() (*Schedstat, error) { | |||
file, err := os.Open(fs.proc.Path("schedstat")) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer file.Close() | |||
stats := &Schedstat{} | |||
scanner := bufio.NewScanner(file) | |||
for scanner.Scan() { | |||
match := cpuLineRE.FindStringSubmatch(scanner.Text()) | |||
if match != nil { | |||
cpu := &SchedstatCPU{} | |||
cpu.CPUNum = match[1] | |||
cpu.RunningNanoseconds, err = strconv.ParseUint(match[8], 10, 64) | |||
if err != nil { | |||
continue | |||
} | |||
cpu.WaitingNanoseconds, err = strconv.ParseUint(match[9], 10, 64) | |||
if err != nil { | |||
continue | |||
} | |||
cpu.RunTimeslices, err = strconv.ParseUint(match[10], 10, 64) | |||
if err != nil { | |||
continue | |||
} | |||
stats.CPUs = append(stats.CPUs, cpu) | |||
} | |||
} | |||
return stats, nil | |||
} | |||
func parseProcSchedstat(contents string) (stats ProcSchedstat, err error) { | |||
match := procLineRE.FindStringSubmatch(contents) | |||
if match != nil { | |||
stats.RunningNanoseconds, err = strconv.ParseUint(match[1], 10, 64) | |||
if err != nil { | |||
return | |||
} | |||
stats.WaitingNanoseconds, err = strconv.ParseUint(match[2], 10, 64) | |||
if err != nil { | |||
return | |||
} | |||
stats.RunTimeslices, err = strconv.ParseUint(match[3], 10, 64) | |||
return | |||
} | |||
err = errors.New("could not parse schedstat") | |||
return | |||
} |
@ -0,0 +1,210 @@ | |||
// Copyright 2019 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. | |||
// +build !windows | |||
package procfs | |||
import ( | |||
"fmt" | |||
"io/ioutil" | |||
"os" | |||
"path/filepath" | |||
"strings" | |||
"github.com/prometheus/procfs/internal/util" | |||
) | |||
// The VM interface is described at | |||
// 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 | |||
type VM struct { | |||
AdminReserveKbytes *int64 // /proc/sys/vm/admin_reserve_kbytes | |||
BlockDump *int64 // /proc/sys/vm/block_dump | |||
CompactUnevictableAllowed *int64 // /proc/sys/vm/compact_unevictable_allowed | |||
DirtyBackgroundBytes *int64 // /proc/sys/vm/dirty_background_bytes | |||
DirtyBackgroundRatio *int64 // /proc/sys/vm/dirty_background_ratio | |||
DirtyBytes *int64 // /proc/sys/vm/dirty_bytes | |||
DirtyExpireCentisecs *int64 // /proc/sys/vm/dirty_expire_centisecs | |||
DirtyRatio *int64 // /proc/sys/vm/dirty_ratio | |||
DirtytimeExpireSeconds *int64 // /proc/sys/vm/dirtytime_expire_seconds | |||
DirtyWritebackCentisecs *int64 // /proc/sys/vm/dirty_writeback_centisecs | |||
DropCaches *int64 // /proc/sys/vm/drop_caches | |||
ExtfragThreshold *int64 // /proc/sys/vm/extfrag_threshold | |||
HugetlbShmGroup *int64 // /proc/sys/vm/hugetlb_shm_group | |||
LaptopMode *int64 // /proc/sys/vm/laptop_mode | |||
LegacyVaLayout *int64 // /proc/sys/vm/legacy_va_layout | |||
LowmemReserveRatio []*int64 // /proc/sys/vm/lowmem_reserve_ratio | |||
MaxMapCount *int64 // /proc/sys/vm/max_map_count | |||
MemoryFailureEarlyKill *int64 // /proc/sys/vm/memory_failure_early_kill | |||
MemoryFailureRecovery *int64 // /proc/sys/vm/memory_failure_recovery | |||
MinFreeKbytes *int64 // /proc/sys/vm/min_free_kbytes | |||
MinSlabRatio *int64 // /proc/sys/vm/min_slab_ratio | |||
MinUnmappedRatio *int64 // /proc/sys/vm/min_unmapped_ratio | |||
MmapMinAddr *int64 // /proc/sys/vm/mmap_min_addr | |||
NrHugepages *int64 // /proc/sys/vm/nr_hugepages | |||
NrHugepagesMempolicy *int64 // /proc/sys/vm/nr_hugepages_mempolicy | |||
NrOvercommitHugepages *int64 // /proc/sys/vm/nr_overcommit_hugepages | |||
NumaStat *int64 // /proc/sys/vm/numa_stat | |||
NumaZonelistOrder string // /proc/sys/vm/numa_zonelist_order | |||
OomDumpTasks *int64 // /proc/sys/vm/oom_dump_tasks | |||
OomKillAllocatingTask *int64 // /proc/sys/vm/oom_kill_allocating_task | |||
OvercommitKbytes *int64 // /proc/sys/vm/overcommit_kbytes | |||
OvercommitMemory *int64 // /proc/sys/vm/overcommit_memory | |||
OvercommitRatio *int64 // /proc/sys/vm/overcommit_ratio | |||
PageCluster *int64 // /proc/sys/vm/page-cluster | |||
PanicOnOom *int64 // /proc/sys/vm/panic_on_oom | |||
PercpuPagelistFraction *int64 // /proc/sys/vm/percpu_pagelist_fraction | |||
StatInterval *int64 // /proc/sys/vm/stat_interval | |||
Swappiness *int64 // /proc/sys/vm/swappiness | |||
UserReserveKbytes *int64 // /proc/sys/vm/user_reserve_kbytes | |||
VfsCachePressure *int64 // /proc/sys/vm/vfs_cache_pressure | |||
WatermarkBoostFactor *int64 // /proc/sys/vm/watermark_boost_factor | |||
WatermarkScaleFactor *int64 // /proc/sys/vm/watermark_scale_factor | |||
ZoneReclaimMode *int64 // /proc/sys/vm/zone_reclaim_mode | |||
} | |||
// VM reads the VM statistics from the specified `proc` filesystem. | |||
func (fs FS) VM() (*VM, error) { | |||
path := fs.proc.Path("sys/vm") | |||
file, err := os.Stat(path) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if !file.Mode().IsDir() { | |||
return nil, fmt.Errorf("%s is not a directory", path) | |||
} | |||
files, err := ioutil.ReadDir(path) | |||
if err != nil { | |||
return nil, err | |||
} | |||
var vm VM | |||
for _, f := range files { | |||
if f.IsDir() { | |||
continue | |||
} | |||
name := filepath.Join(path, f.Name()) | |||
// ignore errors on read, as there are some write only | |||
// in /proc/sys/vm | |||
value, err := util.SysReadFile(name) | |||
if err != nil { | |||
continue | |||
} | |||
vp := util.NewValueParser(value) | |||
switch f.Name() { | |||
case "admin_reserve_kbytes": | |||
vm.AdminReserveKbytes = vp.PInt64() | |||
case "block_dump": | |||
vm.BlockDump = vp.PInt64() | |||
case "compact_unevictable_allowed": | |||
vm.CompactUnevictableAllowed = vp.PInt64() | |||
case "dirty_background_bytes": | |||
vm.DirtyBackgroundBytes = vp.PInt64() | |||
case "dirty_background_ratio": | |||
vm.DirtyBackgroundRatio = vp.PInt64() | |||
case "dirty_bytes": | |||
vm.DirtyBytes = vp.PInt64() | |||
case "dirty_expire_centisecs": | |||
vm.DirtyExpireCentisecs = vp.PInt64() | |||
case "dirty_ratio": | |||
vm.DirtyRatio = vp.PInt64() | |||
case "dirtytime_expire_seconds": | |||
vm.DirtytimeExpireSeconds = vp.PInt64() | |||
case "dirty_writeback_centisecs": | |||
vm.DirtyWritebackCentisecs = vp.PInt64() | |||
case "drop_caches": | |||
vm.DropCaches = vp.PInt64() | |||
case "extfrag_threshold": | |||
vm.ExtfragThreshold = vp.PInt64() | |||
case "hugetlb_shm_group": | |||
vm.HugetlbShmGroup = vp.PInt64() | |||
case "laptop_mode": | |||
vm.LaptopMode = vp.PInt64() | |||
case "legacy_va_layout": | |||
vm.LegacyVaLayout = vp.PInt64() | |||
case "lowmem_reserve_ratio": | |||
stringSlice := strings.Fields(value) | |||
pint64Slice := make([]*int64, 0, len(stringSlice)) | |||
for _, value := range stringSlice { | |||
vp := util.NewValueParser(value) | |||
pint64Slice = append(pint64Slice, vp.PInt64()) | |||
} | |||
vm.LowmemReserveRatio = pint64Slice | |||
case "max_map_count": | |||
vm.MaxMapCount = vp.PInt64() | |||
case "memory_failure_early_kill": | |||
vm.MemoryFailureEarlyKill = vp.PInt64() | |||
case "memory_failure_recovery": | |||
vm.MemoryFailureRecovery = vp.PInt64() | |||
case "min_free_kbytes": | |||
vm.MinFreeKbytes = vp.PInt64() | |||
case "min_slab_ratio": | |||
vm.MinSlabRatio = vp.PInt64() | |||
case "min_unmapped_ratio": | |||
vm.MinUnmappedRatio = vp.PInt64() | |||
case "mmap_min_addr": | |||
vm.MmapMinAddr = vp.PInt64() | |||
case "nr_hugepages": | |||
vm.NrHugepages = vp.PInt64() | |||
case "nr_hugepages_mempolicy": | |||
vm.NrHugepagesMempolicy = vp.PInt64() | |||
case "nr_overcommit_hugepages": | |||
vm.NrOvercommitHugepages = vp.PInt64() | |||
case "numa_stat": | |||
vm.NumaStat = vp.PInt64() | |||
case "numa_zonelist_order": | |||
vm.NumaZonelistOrder = value | |||
case "oom_dump_tasks": | |||
vm.OomDumpTasks = vp.PInt64() | |||
case "oom_kill_allocating_task": | |||
vm.OomKillAllocatingTask = vp.PInt64() | |||
case "overcommit_kbytes": | |||
vm.OvercommitKbytes = vp.PInt64() | |||
case "overcommit_memory": | |||
vm.OvercommitMemory = vp.PInt64() | |||
case "overcommit_ratio": | |||
vm.OvercommitRatio = vp.PInt64() | |||
case "page-cluster": | |||
vm.PageCluster = vp.PInt64() | |||
case "panic_on_oom": | |||
vm.PanicOnOom = vp.PInt64() | |||
case "percpu_pagelist_fraction": | |||
vm.PercpuPagelistFraction = vp.PInt64() | |||
case "stat_interval": | |||
vm.StatInterval = vp.PInt64() | |||
case "swappiness": | |||
vm.Swappiness = vp.PInt64() | |||
case "user_reserve_kbytes": | |||
vm.UserReserveKbytes = vp.PInt64() | |||
case "vfs_cache_pressure": | |||
vm.VfsCachePressure = vp.PInt64() | |||
case "watermark_boost_factor": | |||
vm.WatermarkBoostFactor = vp.PInt64() | |||
case "watermark_scale_factor": | |||
vm.WatermarkScaleFactor = vp.PInt64() | |||
case "zone_reclaim_mode": | |||
vm.ZoneReclaimMode = vp.PInt64() | |||
} | |||
if err := vp.Err(); err != nil { | |||
return nil, err | |||
} | |||
} | |||
return &vm, nil | |||
} |
@ -0,0 +1,196 @@ | |||
// Copyright 2019 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. | |||
// +build !windows | |||
package procfs | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io/ioutil" | |||
"regexp" | |||
"strings" | |||
"github.com/prometheus/procfs/internal/util" | |||
) | |||
// Zoneinfo holds info parsed from /proc/zoneinfo. | |||
type Zoneinfo struct { | |||
Node string | |||
Zone string | |||
NrFreePages *int64 | |||
Min *int64 | |||
Low *int64 | |||
High *int64 | |||
Scanned *int64 | |||
Spanned *int64 | |||
Present *int64 | |||
Managed *int64 | |||
NrActiveAnon *int64 | |||
NrInactiveAnon *int64 | |||
NrIsolatedAnon *int64 | |||
NrAnonPages *int64 | |||
NrAnonTransparentHugepages *int64 | |||
NrActiveFile *int64 | |||
NrInactiveFile *int64 | |||
NrIsolatedFile *int64 | |||
NrFilePages *int64 | |||
NrSlabReclaimable *int64 | |||
NrSlabUnreclaimable *int64 | |||
NrMlockStack *int64 | |||
NrKernelStack *int64 | |||
NrMapped *int64 | |||
NrDirty *int64 | |||
NrWriteback *int64 | |||
NrUnevictable *int64 | |||
NrShmem *int64 | |||
NrDirtied *int64 | |||
NrWritten *int64 | |||
NumaHit *int64 | |||
NumaMiss *int64 | |||
NumaForeign *int64 | |||
NumaInterleave *int64 | |||
NumaLocal *int64 | |||
NumaOther *int64 | |||
Protection []*int64 | |||
} | |||
var nodeZoneRE = regexp.MustCompile(`(\d+), zone\s+(\w+)`) | |||
// Zoneinfo parses an zoneinfo-file (/proc/zoneinfo) and returns a slice of | |||
// structs containing the relevant info. More information available here: | |||
// https://www.kernel.org/doc/Documentation/sysctl/vm.txt | |||
func (fs FS) Zoneinfo() ([]Zoneinfo, error) { | |||
data, err := ioutil.ReadFile(fs.proc.Path("zoneinfo")) | |||
if err != nil { | |||
return nil, fmt.Errorf("error reading zoneinfo %s: %s", fs.proc.Path("zoneinfo"), err) | |||
} | |||
zoneinfo, err := parseZoneinfo(data) | |||
if err != nil { | |||
return nil, fmt.Errorf("error parsing zoneinfo %s: %s", fs.proc.Path("zoneinfo"), err) | |||
} | |||
return zoneinfo, nil | |||
} | |||
func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) { | |||
zoneinfo := []Zoneinfo{} | |||
zoneinfoBlocks := bytes.Split(zoneinfoData, []byte("\nNode")) | |||
for _, block := range zoneinfoBlocks { | |||
var zoneinfoElement Zoneinfo | |||
lines := strings.Split(string(block), "\n") | |||
for _, line := range lines { | |||
if nodeZone := nodeZoneRE.FindStringSubmatch(line); nodeZone != nil { | |||
zoneinfoElement.Node = nodeZone[1] | |||
zoneinfoElement.Zone = nodeZone[2] | |||
continue | |||
} | |||
if strings.HasPrefix(strings.TrimSpace(line), "per-node stats") { | |||
zoneinfoElement.Zone = "" | |||
continue | |||
} | |||
parts := strings.Fields(strings.TrimSpace(line)) | |||
if len(parts) < 2 { | |||
continue | |||
} | |||
vp := util.NewValueParser(parts[1]) | |||
switch parts[0] { | |||
case "nr_free_pages": | |||
zoneinfoElement.NrFreePages = vp.PInt64() | |||
case "min": | |||
zoneinfoElement.Min = vp.PInt64() | |||
case "low": | |||
zoneinfoElement.Low = vp.PInt64() | |||
case "high": | |||
zoneinfoElement.High = vp.PInt64() | |||
case "scanned": | |||
zoneinfoElement.Scanned = vp.PInt64() | |||
case "spanned": | |||
zoneinfoElement.Spanned = vp.PInt64() | |||
case "present": | |||
zoneinfoElement.Present = vp.PInt64() | |||
case "managed": | |||
zoneinfoElement.Managed = vp.PInt64() | |||
case "nr_active_anon": | |||
zoneinfoElement.NrActiveAnon = vp.PInt64() | |||
case "nr_inactive_anon": | |||
zoneinfoElement.NrInactiveAnon = vp.PInt64() | |||
case "nr_isolated_anon": | |||
zoneinfoElement.NrIsolatedAnon = vp.PInt64() | |||
case "nr_anon_pages": | |||
zoneinfoElement.NrAnonPages = vp.PInt64() | |||
case "nr_anon_transparent_hugepages": | |||
zoneinfoElement.NrAnonTransparentHugepages = vp.PInt64() | |||
case "nr_active_file": | |||
zoneinfoElement.NrActiveFile = vp.PInt64() | |||
case "nr_inactive_file": | |||
zoneinfoElement.NrInactiveFile = vp.PInt64() | |||
case "nr_isolated_file": | |||
zoneinfoElement.NrIsolatedFile = vp.PInt64() | |||
case "nr_file_pages": | |||
zoneinfoElement.NrFilePages = vp.PInt64() | |||
case "nr_slab_reclaimable": | |||
zoneinfoElement.NrSlabReclaimable = vp.PInt64() | |||
case "nr_slab_unreclaimable": | |||
zoneinfoElement.NrSlabUnreclaimable = vp.PInt64() | |||
case "nr_mlock_stack": | |||
zoneinfoElement.NrMlockStack = vp.PInt64() | |||
case "nr_kernel_stack": | |||
zoneinfoElement.NrKernelStack = vp.PInt64() | |||
case "nr_mapped": | |||
zoneinfoElement.NrMapped = vp.PInt64() | |||
case "nr_dirty": | |||
zoneinfoElement.NrDirty = vp.PInt64() | |||
case "nr_writeback": | |||
zoneinfoElement.NrWriteback = vp.PInt64() | |||
case "nr_unevictable": | |||
zoneinfoElement.NrUnevictable = vp.PInt64() | |||
case "nr_shmem": | |||
zoneinfoElement.NrShmem = vp.PInt64() | |||
case "nr_dirtied": | |||
zoneinfoElement.NrDirtied = vp.PInt64() | |||
case "nr_written": | |||
zoneinfoElement.NrWritten = vp.PInt64() | |||
case "numa_hit": | |||
zoneinfoElement.NumaHit = vp.PInt64() | |||
case "numa_miss": | |||
zoneinfoElement.NumaMiss = vp.PInt64() | |||
case "numa_foreign": | |||
zoneinfoElement.NumaForeign = vp.PInt64() | |||
case "numa_interleave": | |||
zoneinfoElement.NumaInterleave = vp.PInt64() | |||
case "numa_local": | |||
zoneinfoElement.NumaLocal = vp.PInt64() | |||
case "numa_other": | |||
zoneinfoElement.NumaOther = vp.PInt64() | |||
case "protection:": | |||
protectionParts := strings.Split(line, ":") | |||
protectionValues := strings.Replace(protectionParts[1], "(", "", 1) | |||
protectionValues = strings.Replace(protectionValues, ")", "", 1) | |||
protectionValues = strings.TrimSpace(protectionValues) | |||
protectionStringMap := strings.Split(protectionValues, ", ") | |||
val, err := util.ParsePInt64s(protectionStringMap) | |||
if err == nil { | |||
zoneinfoElement.Protection = val | |||
} | |||
} | |||
} | |||
zoneinfo = append(zoneinfo, zoneinfoElement) | |||
} | |||
return zoneinfo, nil | |||
} |
@ -0,0 +1,309 @@ | |||
package assert | |||
import ( | |||
"fmt" | |||
"reflect" | |||
) | |||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { | |||
switch kind { | |||
case reflect.Int: | |||
{ | |||
intobj1 := obj1.(int) | |||
intobj2 := obj2.(int) | |||
if intobj1 > intobj2 { | |||
return -1, true | |||
} | |||
if intobj1 == intobj2 { | |||
return 0, true | |||
} | |||
if intobj1 < intobj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Int8: | |||
{ | |||
int8obj1 := obj1.(int8) | |||
int8obj2 := obj2.(int8) | |||
if int8obj1 > int8obj2 { | |||
return -1, true | |||
} | |||
if int8obj1 == int8obj2 { | |||
return 0, true | |||
} | |||
if int8obj1 < int8obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Int16: | |||
{ | |||
int16obj1 := obj1.(int16) | |||
int16obj2 := obj2.(int16) | |||
if int16obj1 > int16obj2 { | |||
return -1, true | |||
} | |||
if int16obj1 == int16obj2 { | |||
return 0, true | |||
} | |||
if int16obj1 < int16obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Int32: | |||
{ | |||
int32obj1 := obj1.(int32) | |||
int32obj2 := obj2.(int32) | |||
if int32obj1 > int32obj2 { | |||
return -1, true | |||
} | |||
if int32obj1 == int32obj2 { | |||
return 0, true | |||
} | |||
if int32obj1 < int32obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Int64: | |||
{ | |||
int64obj1 := obj1.(int64) | |||
int64obj2 := obj2.(int64) | |||
if int64obj1 > int64obj2 { | |||
return -1, true | |||
} | |||
if int64obj1 == int64obj2 { | |||
return 0, true | |||
} | |||
if int64obj1 < int64obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Uint: | |||
{ | |||
uintobj1 := obj1.(uint) | |||
uintobj2 := obj2.(uint) | |||
if uintobj1 > uintobj2 { | |||
return -1, true | |||
} | |||
if uintobj1 == uintobj2 { | |||
return 0, true | |||
} | |||
if uintobj1 < uintobj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Uint8: | |||
{ | |||
uint8obj1 := obj1.(uint8) | |||
uint8obj2 := obj2.(uint8) | |||
if uint8obj1 > uint8obj2 { | |||
return -1, true | |||
} | |||
if uint8obj1 == uint8obj2 { | |||
return 0, true | |||
} | |||
if uint8obj1 < uint8obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Uint16: | |||
{ | |||
uint16obj1 := obj1.(uint16) | |||
uint16obj2 := obj2.(uint16) | |||
if uint16obj1 > uint16obj2 { | |||
return -1, true | |||
} | |||
if uint16obj1 == uint16obj2 { | |||
return 0, true | |||
} | |||
if uint16obj1 < uint16obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Uint32: | |||
{ | |||
uint32obj1 := obj1.(uint32) | |||
uint32obj2 := obj2.(uint32) | |||
if uint32obj1 > uint32obj2 { | |||
return -1, true | |||
} | |||
if uint32obj1 == uint32obj2 { | |||
return 0, true | |||
} | |||
if uint32obj1 < uint32obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Uint64: | |||
{ | |||
uint64obj1 := obj1.(uint64) | |||
uint64obj2 := obj2.(uint64) | |||
if uint64obj1 > uint64obj2 { | |||
return -1, true | |||
} | |||
if uint64obj1 == uint64obj2 { | |||
return 0, true | |||
} | |||
if uint64obj1 < uint64obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Float32: | |||
{ | |||
float32obj1 := obj1.(float32) | |||
float32obj2 := obj2.(float32) | |||
if float32obj1 > float32obj2 { | |||
return -1, true | |||
} | |||
if float32obj1 == float32obj2 { | |||
return 0, true | |||
} | |||
if float32obj1 < float32obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.Float64: | |||
{ | |||
float64obj1 := obj1.(float64) | |||
float64obj2 := obj2.(float64) | |||
if float64obj1 > float64obj2 { | |||
return -1, true | |||
} | |||
if float64obj1 == float64obj2 { | |||
return 0, true | |||
} | |||
if float64obj1 < float64obj2 { | |||
return 1, true | |||
} | |||
} | |||
case reflect.String: | |||
{ | |||
stringobj1 := obj1.(string) | |||
stringobj2 := obj2.(string) | |||
if stringobj1 > stringobj2 { | |||
return -1, true | |||
} | |||
if stringobj1 == stringobj2 { | |||
return 0, true | |||
} | |||
if stringobj1 < stringobj2 { | |||
return 1, true | |||
} | |||
} | |||
} | |||
return 0, false | |||
} | |||
// Greater asserts that the first element is greater than the second | |||
// | |||
// assert.Greater(t, 2, 1) | |||
// assert.Greater(t, float64(2), float64(1)) | |||
// assert.Greater(t, "b", "a") | |||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
if h, ok := t.(tHelper); ok { | |||
h.Helper() | |||
} | |||
e1Kind := reflect.ValueOf(e1).Kind() | |||
e2Kind := reflect.ValueOf(e2).Kind() | |||
if e1Kind != e2Kind { | |||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
} | |||
res, isComparable := compare(e1, e2, e1Kind) | |||
if !isComparable { | |||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
} | |||
if res != -1 { | |||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) | |||
} | |||
return true | |||
} | |||
// GreaterOrEqual asserts that the first element is greater than or equal to the second | |||
// | |||
// assert.GreaterOrEqual(t, 2, 1) | |||
// assert.GreaterOrEqual(t, 2, 2) | |||
// assert.GreaterOrEqual(t, "b", "a") | |||
// assert.GreaterOrEqual(t, "b", "b") | |||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
if h, ok := t.(tHelper); ok { | |||
h.Helper() | |||
} | |||
e1Kind := reflect.ValueOf(e1).Kind() | |||
e2Kind := reflect.ValueOf(e2).Kind() | |||
if e1Kind != e2Kind { | |||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
} | |||
res, isComparable := compare(e1, e2, e1Kind) | |||
if !isComparable { | |||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
} | |||
if res != -1 && res != 0 { | |||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||
} | |||
return true | |||
} | |||
// Less asserts that the first element is less than the second | |||
// | |||
// assert.Less(t, 1, 2) | |||
// assert.Less(t, float64(1), float64(2)) | |||
// assert.Less(t, "a", "b") | |||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
if h, ok := t.(tHelper); ok { | |||
h.Helper() | |||
} | |||
e1Kind := reflect.ValueOf(e1).Kind() | |||
e2Kind := reflect.ValueOf(e2).Kind() | |||
if e1Kind != e2Kind { | |||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
} | |||
res, isComparable := compare(e1, e2, e1Kind) | |||
if !isComparable { | |||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
} | |||
if res != 1 { | |||
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) | |||
} | |||
return true | |||
} | |||
// LessOrEqual asserts that the first element is less than or equal to the second | |||
// | |||
// assert.LessOrEqual(t, 1, 2) | |||
// assert.LessOrEqual(t, 2, 2) | |||
// assert.LessOrEqual(t, "a", "b") | |||
// assert.LessOrEqual(t, "b", "b") | |||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||
if h, ok := t.(tHelper); ok { | |||
h.Helper() | |||
} | |||
e1Kind := reflect.ValueOf(e1).Kind() | |||
e2Kind := reflect.ValueOf(e2).Kind() | |||
if e1Kind != e2Kind { | |||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||
} | |||
res, isComparable := compare(e1, e2, e1Kind) | |||
if !isComparable { | |||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||
} | |||
if res != 1 && res != 0 { | |||
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||
} | |||
return true | |||
} |
@ -1,6 +1,6 @@ | |||
{{.Comment}} | |||
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { | |||
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } | |||
if h, ok := t.(tHelper); ok { h.Helper() } | |||
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } | |||
t.FailNow() | |||
} |