Add vendoring
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 50s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 50s
This commit is contained in:
22
vendor/github.com/VictoriaMetrics/metrics/LICENSE
generated
vendored
Normal file
22
vendor/github.com/VictoriaMetrics/metrics/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 VictoriaMetrics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
116
vendor/github.com/VictoriaMetrics/metrics/README.md
generated
vendored
Normal file
116
vendor/github.com/VictoriaMetrics/metrics/README.md
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
[](https://github.com/VictoriaMetrics/metrics/actions)
|
||||
[](http://godoc.org/github.com/VictoriaMetrics/metrics)
|
||||
[](https://goreportcard.com/report/github.com/VictoriaMetrics/metrics)
|
||||
[](https://codecov.io/gh/VictoriaMetrics/metrics)
|
||||
|
||||
|
||||
# metrics - lightweight package for exporting metrics in Prometheus format
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Lightweight. Has minimal number of third-party dependencies and all these deps are small.
|
||||
See [this article](https://medium.com/@valyala/stripping-dependency-bloat-in-victoriametrics-docker-image-983fb5912b0d) for details.
|
||||
* Easy to use. See the [API docs](http://godoc.org/github.com/VictoriaMetrics/metrics).
|
||||
* Fast.
|
||||
* Allows exporting distinct metric sets via distinct endpoints. See [Set](http://godoc.org/github.com/VictoriaMetrics/metrics#Set).
|
||||
* Supports [easy-to-use histograms](http://godoc.org/github.com/VictoriaMetrics/metrics#Histogram), which just work without any tuning.
|
||||
Read more about VictoriaMetrics histograms at [this article](https://medium.com/@valyala/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350).
|
||||
* Can push metrics to VictoriaMetrics or to any other remote storage, which accepts metrics
|
||||
in [Prometheus text exposition format](https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format).
|
||||
See [these docs](http://godoc.org/github.com/VictoriaMetrics/metrics#InitPush).
|
||||
|
||||
|
||||
### Limitations
|
||||
|
||||
* It doesn't implement advanced functionality from [github.com/prometheus/client_golang](https://godoc.org/github.com/prometheus/client_golang).
|
||||
|
||||
|
||||
### Usage
|
||||
|
||||
```go
|
||||
import "github.com/VictoriaMetrics/metrics"
|
||||
|
||||
// Register various metrics.
|
||||
// Metric name may contain labels in Prometheus format - see below.
|
||||
var (
|
||||
// Register counter without labels.
|
||||
requestsTotal = metrics.NewCounter("requests_total")
|
||||
|
||||
// Register summary with a single label.
|
||||
requestDuration = metrics.NewSummary(`requests_duration_seconds{path="/foobar/baz"}`)
|
||||
|
||||
// Register gauge with two labels.
|
||||
queueSize = metrics.NewGauge(`queue_size{queue="foobar",topic="baz"}`, func() float64 {
|
||||
return float64(foobarQueue.Len())
|
||||
})
|
||||
|
||||
// Register histogram with a single label.
|
||||
responseSize = metrics.NewHistogram(`response_size{path="/foo/bar"}`)
|
||||
)
|
||||
|
||||
// ...
|
||||
func requestHandler() {
|
||||
// Increment requestTotal counter.
|
||||
requestsTotal.Inc()
|
||||
|
||||
startTime := time.Now()
|
||||
processRequest()
|
||||
// Update requestDuration summary.
|
||||
requestDuration.UpdateDuration(startTime)
|
||||
|
||||
// Update responseSize histogram.
|
||||
responseSize.Update(responseSize)
|
||||
}
|
||||
|
||||
// Expose the registered metrics at `/metrics` path.
|
||||
http.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
|
||||
metrics.WritePrometheus(w, true)
|
||||
})
|
||||
|
||||
// ... or push registered metrics every 10 seconds to http://victoria-metrics:8428/api/v1/import/prometheus
|
||||
// with the added `instance="foobar"` label to all the pushed metrics.
|
||||
metrics.InitPush("http://victoria-metrics:8428/api/v1/import/prometheus", 10*time.Second, `instance="foobar"`, true)
|
||||
```
|
||||
|
||||
By default, exposed metrics [do not have](https://github.com/VictoriaMetrics/metrics/issues/48#issuecomment-1620765811)
|
||||
`TYPE` or `HELP` meta information. Call [`ExposeMetadata(true)`](https://pkg.go.dev/github.com/VictoriaMetrics/metrics#ExposeMetadata)
|
||||
in order to generate `TYPE` and `HELP` meta information per each metric.
|
||||
|
||||
See [docs](https://pkg.go.dev/github.com/VictoriaMetrics/metrics) for more info.
|
||||
|
||||
### Users
|
||||
|
||||
* `Metrics` has been extracted from [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) sources.
|
||||
See [this article](https://medium.com/devopslinks/victoriametrics-creating-the-best-remote-storage-for-prometheus-5d92d66787ac)
|
||||
for more info about `VictoriaMetrics`.
|
||||
|
||||
|
||||
### FAQ
|
||||
|
||||
#### Why the `metrics` API isn't compatible with `github.com/prometheus/client_golang`?
|
||||
|
||||
Because the `github.com/prometheus/client_golang` is too complex and is hard to use.
|
||||
|
||||
|
||||
#### Why the `metrics.WritePrometheus` doesn't expose documentation for each metric?
|
||||
|
||||
Because this documentation is ignored by Prometheus. The documentation is for users.
|
||||
Just give [meaningful names to the exported metrics](https://prometheus.io/docs/practices/naming/#metric-names)
|
||||
or add comments in the source code or in other suitable place explaining each metric exposed from your application.
|
||||
|
||||
|
||||
#### How to implement [CounterVec](https://godoc.org/github.com/prometheus/client_golang/prometheus#CounterVec) in `metrics`?
|
||||
|
||||
Just use [GetOrCreateCounter](http://godoc.org/github.com/VictoriaMetrics/metrics#GetOrCreateCounter)
|
||||
instead of `CounterVec.With`. See [this example](https://pkg.go.dev/github.com/VictoriaMetrics/metrics#example-Counter-Vec) for details.
|
||||
|
||||
|
||||
#### Why [Histogram](http://godoc.org/github.com/VictoriaMetrics/metrics#Histogram) buckets contain `vmrange` labels instead of `le` labels like in Prometheus histograms?
|
||||
|
||||
Buckets with `vmrange` labels occupy less disk space compared to Prometheus-style buckets with `le` labels,
|
||||
because `vmrange` buckets don't include counters for the previous ranges. [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) provides `prometheus_buckets`
|
||||
function, which converts `vmrange` buckets to Prometheus-style buckets with `le` labels. This is useful for building heatmaps in Grafana.
|
||||
Additionally, its `histogram_quantile` function transparently handles histogram buckets with `vmrange` labels.
|
||||
|
||||
However, for compatibility purposes package provides classic [Prometheus Histograms](http://godoc.org/github.com/VictoriaMetrics/metrics#PrometheusHistogram) with `le` labels.
|
||||
86
vendor/github.com/VictoriaMetrics/metrics/counter.go
generated
vendored
Normal file
86
vendor/github.com/VictoriaMetrics/metrics/counter.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// NewCounter registers and returns new counter with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned counter is safe to use from concurrent goroutines.
|
||||
func NewCounter(name string) *Counter {
|
||||
return defaultSet.NewCounter(name)
|
||||
}
|
||||
|
||||
// Counter is a counter.
|
||||
//
|
||||
// It may be used as a gauge if Dec and Set are called.
|
||||
type Counter struct {
|
||||
n uint64
|
||||
}
|
||||
|
||||
// Inc increments c.
|
||||
func (c *Counter) Inc() {
|
||||
atomic.AddUint64(&c.n, 1)
|
||||
}
|
||||
|
||||
// Dec decrements c.
|
||||
func (c *Counter) Dec() {
|
||||
atomic.AddUint64(&c.n, ^uint64(0))
|
||||
}
|
||||
|
||||
// Add adds n to c.
|
||||
func (c *Counter) Add(n int) {
|
||||
atomic.AddUint64(&c.n, uint64(n))
|
||||
}
|
||||
|
||||
// AddInt64 adds n to c.
|
||||
func (c *Counter) AddInt64(n int64) {
|
||||
atomic.AddUint64(&c.n, uint64(n))
|
||||
}
|
||||
|
||||
// Get returns the current value for c.
|
||||
func (c *Counter) Get() uint64 {
|
||||
return atomic.LoadUint64(&c.n)
|
||||
}
|
||||
|
||||
// Set sets c value to n.
|
||||
func (c *Counter) Set(n uint64) {
|
||||
atomic.StoreUint64(&c.n, n)
|
||||
}
|
||||
|
||||
// marshalTo marshals c with the given prefix to w.
|
||||
func (c *Counter) marshalTo(prefix string, w io.Writer) {
|
||||
v := c.Get()
|
||||
fmt.Fprintf(w, "%s %d\n", prefix, v)
|
||||
}
|
||||
|
||||
func (c *Counter) metricType() string {
|
||||
return "counter"
|
||||
}
|
||||
|
||||
// GetOrCreateCounter returns registered counter with the given name
|
||||
// or creates new counter if the registry doesn't contain counter with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned counter is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewCounter instead of GetOrCreateCounter.
|
||||
func GetOrCreateCounter(name string) *Counter {
|
||||
return defaultSet.GetOrCreateCounter(name)
|
||||
}
|
||||
86
vendor/github.com/VictoriaMetrics/metrics/floatcounter.go
generated
vendored
Normal file
86
vendor/github.com/VictoriaMetrics/metrics/floatcounter.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewFloatCounter registers and returns new counter of float64 type with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned counter is safe to use from concurrent goroutines.
|
||||
func NewFloatCounter(name string) *FloatCounter {
|
||||
return defaultSet.NewFloatCounter(name)
|
||||
}
|
||||
|
||||
// FloatCounter is a float64 counter guarded by RWmutex.
|
||||
//
|
||||
// It may be used as a gauge if Add and Sub are called.
|
||||
type FloatCounter struct {
|
||||
mu sync.Mutex
|
||||
n float64
|
||||
}
|
||||
|
||||
// Add adds n to fc.
|
||||
func (fc *FloatCounter) Add(n float64) {
|
||||
fc.mu.Lock()
|
||||
fc.n += n
|
||||
fc.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sub substracts n from fc.
|
||||
func (fc *FloatCounter) Sub(n float64) {
|
||||
fc.mu.Lock()
|
||||
fc.n -= n
|
||||
fc.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get returns the current value for fc.
|
||||
func (fc *FloatCounter) Get() float64 {
|
||||
fc.mu.Lock()
|
||||
n := fc.n
|
||||
fc.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
// Set sets fc value to n.
|
||||
func (fc *FloatCounter) Set(n float64) {
|
||||
fc.mu.Lock()
|
||||
fc.n = n
|
||||
fc.mu.Unlock()
|
||||
}
|
||||
|
||||
// marshalTo marshals fc with the given prefix to w.
|
||||
func (fc *FloatCounter) marshalTo(prefix string, w io.Writer) {
|
||||
v := fc.Get()
|
||||
fmt.Fprintf(w, "%s %g\n", prefix, v)
|
||||
}
|
||||
|
||||
func (fc *FloatCounter) metricType() string {
|
||||
return "counter"
|
||||
}
|
||||
|
||||
// GetOrCreateFloatCounter returns registered FloatCounter with the given name
|
||||
// or creates new FloatCounter if the registry doesn't contain FloatCounter with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned FloatCounter is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewFloatCounter instead of GetOrCreateFloatCounter.
|
||||
func GetOrCreateFloatCounter(name string) *FloatCounter {
|
||||
return defaultSet.GetOrCreateFloatCounter(name)
|
||||
}
|
||||
122
vendor/github.com/VictoriaMetrics/metrics/gauge.go
generated
vendored
Normal file
122
vendor/github.com/VictoriaMetrics/metrics/gauge.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// NewGauge registers and returns gauge with the given name, which calls f to obtain gauge value.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// f must be safe for concurrent calls.
|
||||
// if f is nil, then it is expected that the gauge value is changed via Set(), Inc(), Dec() and Add() calls.
|
||||
//
|
||||
// The returned gauge is safe to use from concurrent goroutines.
|
||||
//
|
||||
// See also FloatCounter for working with floating-point values.
|
||||
func NewGauge(name string, f func() float64) *Gauge {
|
||||
return defaultSet.NewGauge(name, f)
|
||||
}
|
||||
|
||||
// Gauge is a float64 gauge.
|
||||
type Gauge struct {
|
||||
// valueBits contains uint64 representation of float64 passed to Gauge.Set.
|
||||
valueBits uint64
|
||||
|
||||
// f is a callback, which is called for returning the gauge value.
|
||||
f func() float64
|
||||
}
|
||||
|
||||
// Get returns the current value for g.
|
||||
func (g *Gauge) Get() float64 {
|
||||
if f := g.f; f != nil {
|
||||
return f()
|
||||
}
|
||||
n := atomic.LoadUint64(&g.valueBits)
|
||||
return math.Float64frombits(n)
|
||||
}
|
||||
|
||||
// Set sets g value to v.
|
||||
//
|
||||
// The g must be created with nil callback in order to be able to call this function.
|
||||
func (g *Gauge) Set(v float64) {
|
||||
if g.f != nil {
|
||||
panic(fmt.Errorf("cannot call Set on gauge created with non-nil callback"))
|
||||
}
|
||||
n := math.Float64bits(v)
|
||||
atomic.StoreUint64(&g.valueBits, n)
|
||||
}
|
||||
|
||||
// Inc increments g by 1.
|
||||
//
|
||||
// The g must be created with nil callback in order to be able to call this function.
|
||||
func (g *Gauge) Inc() {
|
||||
g.Add(1)
|
||||
}
|
||||
|
||||
// Dec decrements g by 1.
|
||||
//
|
||||
// The g must be created with nil callback in order to be able to call this function.
|
||||
func (g *Gauge) Dec() {
|
||||
g.Add(-1)
|
||||
}
|
||||
|
||||
// Add adds fAdd to g. fAdd may be positive and negative.
|
||||
//
|
||||
// The g must be created with nil callback in order to be able to call this function.
|
||||
func (g *Gauge) Add(fAdd float64) {
|
||||
if g.f != nil {
|
||||
panic(fmt.Errorf("cannot call Set on gauge created with non-nil callback"))
|
||||
}
|
||||
for {
|
||||
n := atomic.LoadUint64(&g.valueBits)
|
||||
f := math.Float64frombits(n)
|
||||
fNew := f + fAdd
|
||||
nNew := math.Float64bits(fNew)
|
||||
if atomic.CompareAndSwapUint64(&g.valueBits, n, nNew) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gauge) marshalTo(prefix string, w io.Writer) {
|
||||
v := g.Get()
|
||||
if float64(int64(v)) == v {
|
||||
// Marshal integer values without scientific notation
|
||||
fmt.Fprintf(w, "%s %d\n", prefix, int64(v))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s %g\n", prefix, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Gauge) metricType() string {
|
||||
return "gauge"
|
||||
}
|
||||
|
||||
// GetOrCreateGauge returns registered gauge with the given name
|
||||
// or creates new gauge if the registry doesn't contain gauge with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned gauge is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewGauge instead of GetOrCreateGauge.
|
||||
//
|
||||
// See also FloatCounter for working with floating-point values.
|
||||
func GetOrCreateGauge(name string, f func() float64) *Gauge {
|
||||
return defaultSet.GetOrCreateGauge(name, f)
|
||||
}
|
||||
192
vendor/github.com/VictoriaMetrics/metrics/go_metrics.go
generated
vendored
Normal file
192
vendor/github.com/VictoriaMetrics/metrics/go_metrics.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"runtime"
|
||||
runtimemetrics "runtime/metrics"
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/histogram"
|
||||
)
|
||||
|
||||
// See https://pkg.go.dev/runtime/metrics#hdr-Supported_metrics
|
||||
var runtimeMetrics = [][2]string{
|
||||
{"/sched/latencies:seconds", "go_sched_latencies_seconds"},
|
||||
{"/sync/mutex/wait/total:seconds", "go_mutex_wait_seconds_total"},
|
||||
{"/cpu/classes/gc/mark/assist:cpu-seconds", "go_gc_mark_assist_cpu_seconds_total"},
|
||||
{"/cpu/classes/gc/total:cpu-seconds", "go_gc_cpu_seconds_total"},
|
||||
{"/gc/pauses:seconds", "go_gc_pauses_seconds"},
|
||||
{"/cpu/classes/scavenge/total:cpu-seconds", "go_scavenge_cpu_seconds_total"},
|
||||
{"/gc/gomemlimit:bytes", "go_memlimit_bytes"},
|
||||
}
|
||||
|
||||
var supportedRuntimeMetrics = initSupportedRuntimeMetrics(runtimeMetrics)
|
||||
|
||||
func initSupportedRuntimeMetrics(rms [][2]string) [][2]string {
|
||||
exposedMetrics := make(map[string]struct{})
|
||||
for _, d := range runtimemetrics.All() {
|
||||
exposedMetrics[d.Name] = struct{}{}
|
||||
}
|
||||
var supportedMetrics [][2]string
|
||||
for _, rm := range rms {
|
||||
metricName := rm[0]
|
||||
if _, ok := exposedMetrics[metricName]; ok {
|
||||
supportedMetrics = append(supportedMetrics, rm)
|
||||
} else {
|
||||
log.Printf("github.com/VictoriaMetrics/metrics: do not expose %s metric, since the corresponding metric %s isn't supported in the current Go runtime", rm[1], metricName)
|
||||
}
|
||||
}
|
||||
return supportedMetrics
|
||||
}
|
||||
|
||||
func writeGoMetrics(w io.Writer) {
|
||||
writeRuntimeMetrics(w)
|
||||
|
||||
var ms runtime.MemStats
|
||||
runtime.ReadMemStats(&ms)
|
||||
WriteGaugeUint64(w, "go_memstats_alloc_bytes", ms.Alloc)
|
||||
WriteCounterUint64(w, "go_memstats_alloc_bytes_total", ms.TotalAlloc)
|
||||
WriteGaugeUint64(w, "go_memstats_buck_hash_sys_bytes", ms.BuckHashSys)
|
||||
WriteCounterUint64(w, "go_memstats_frees_total", ms.Frees)
|
||||
WriteGaugeFloat64(w, "go_memstats_gc_cpu_fraction", ms.GCCPUFraction)
|
||||
WriteGaugeUint64(w, "go_memstats_gc_sys_bytes", ms.GCSys)
|
||||
|
||||
WriteGaugeUint64(w, "go_memstats_heap_alloc_bytes", ms.HeapAlloc)
|
||||
WriteGaugeUint64(w, "go_memstats_heap_idle_bytes", ms.HeapIdle)
|
||||
WriteGaugeUint64(w, "go_memstats_heap_inuse_bytes", ms.HeapInuse)
|
||||
WriteGaugeUint64(w, "go_memstats_heap_objects", ms.HeapObjects)
|
||||
WriteGaugeUint64(w, "go_memstats_heap_released_bytes", ms.HeapReleased)
|
||||
WriteGaugeUint64(w, "go_memstats_heap_sys_bytes", ms.HeapSys)
|
||||
WriteGaugeFloat64(w, "go_memstats_last_gc_time_seconds", float64(ms.LastGC)/1e9)
|
||||
WriteCounterUint64(w, "go_memstats_lookups_total", ms.Lookups)
|
||||
WriteCounterUint64(w, "go_memstats_mallocs_total", ms.Mallocs)
|
||||
WriteGaugeUint64(w, "go_memstats_mcache_inuse_bytes", ms.MCacheInuse)
|
||||
WriteGaugeUint64(w, "go_memstats_mcache_sys_bytes", ms.MCacheSys)
|
||||
WriteGaugeUint64(w, "go_memstats_mspan_inuse_bytes", ms.MSpanInuse)
|
||||
WriteGaugeUint64(w, "go_memstats_mspan_sys_bytes", ms.MSpanSys)
|
||||
WriteGaugeUint64(w, "go_memstats_next_gc_bytes", ms.NextGC)
|
||||
WriteGaugeUint64(w, "go_memstats_other_sys_bytes", ms.OtherSys)
|
||||
WriteGaugeUint64(w, "go_memstats_stack_inuse_bytes", ms.StackInuse)
|
||||
WriteGaugeUint64(w, "go_memstats_stack_sys_bytes", ms.StackSys)
|
||||
WriteGaugeUint64(w, "go_memstats_sys_bytes", ms.Sys)
|
||||
|
||||
WriteCounterUint64(w, "go_cgo_calls_count", uint64(runtime.NumCgoCall()))
|
||||
WriteGaugeUint64(w, "go_cpu_count", uint64(runtime.NumCPU()))
|
||||
|
||||
gcPauses := histogram.NewFast()
|
||||
for _, pauseNs := range ms.PauseNs[:] {
|
||||
gcPauses.Update(float64(pauseNs) / 1e9)
|
||||
}
|
||||
phis := []float64{0, 0.25, 0.5, 0.75, 1}
|
||||
quantiles := make([]float64, 0, len(phis))
|
||||
WriteMetadataIfNeeded(w, "go_gc_duration_seconds", "summary")
|
||||
for i, q := range gcPauses.Quantiles(quantiles[:0], phis) {
|
||||
fmt.Fprintf(w, `go_gc_duration_seconds{quantile="%g"} %g`+"\n", phis[i], q)
|
||||
}
|
||||
fmt.Fprintf(w, "go_gc_duration_seconds_sum %g\n", float64(ms.PauseTotalNs)/1e9)
|
||||
fmt.Fprintf(w, "go_gc_duration_seconds_count %d\n", ms.NumGC)
|
||||
|
||||
WriteCounterUint64(w, "go_gc_forced_count", uint64(ms.NumForcedGC))
|
||||
|
||||
WriteGaugeUint64(w, "go_gomaxprocs", uint64(runtime.GOMAXPROCS(0)))
|
||||
WriteGaugeUint64(w, "go_goroutines", uint64(runtime.NumGoroutine()))
|
||||
numThread, _ := runtime.ThreadCreateProfile(nil)
|
||||
WriteGaugeUint64(w, "go_threads", uint64(numThread))
|
||||
|
||||
// Export build details.
|
||||
WriteMetadataIfNeeded(w, "go_info", "gauge")
|
||||
fmt.Fprintf(w, "go_info{version=%q} 1\n", runtime.Version())
|
||||
|
||||
WriteMetadataIfNeeded(w, "go_info_ext", "gauge")
|
||||
fmt.Fprintf(w, "go_info_ext{compiler=%q, GOARCH=%q, GOOS=%q, GOROOT=%q} 1\n",
|
||||
runtime.Compiler, runtime.GOARCH, runtime.GOOS, runtime.GOROOT())
|
||||
}
|
||||
|
||||
func writeRuntimeMetrics(w io.Writer) {
|
||||
samples := make([]runtimemetrics.Sample, len(supportedRuntimeMetrics))
|
||||
for i, rm := range supportedRuntimeMetrics {
|
||||
samples[i].Name = rm[0]
|
||||
}
|
||||
runtimemetrics.Read(samples)
|
||||
for i, rm := range supportedRuntimeMetrics {
|
||||
writeRuntimeMetric(w, rm[1], &samples[i])
|
||||
}
|
||||
}
|
||||
|
||||
func writeRuntimeMetric(w io.Writer, name string, sample *runtimemetrics.Sample) {
|
||||
kind := sample.Value.Kind()
|
||||
switch kind {
|
||||
case runtimemetrics.KindBad:
|
||||
panic(fmt.Errorf("BUG: unexpected runtimemetrics.KindBad for sample.Name=%q", sample.Name))
|
||||
case runtimemetrics.KindUint64:
|
||||
v := sample.Value.Uint64()
|
||||
if strings.HasSuffix(name, "_total") {
|
||||
WriteCounterUint64(w, name, v)
|
||||
} else {
|
||||
WriteGaugeUint64(w, name, v)
|
||||
}
|
||||
case runtimemetrics.KindFloat64:
|
||||
v := sample.Value.Float64()
|
||||
if isCounterName(name) {
|
||||
WriteCounterFloat64(w, name, v)
|
||||
} else {
|
||||
WriteGaugeFloat64(w, name, v)
|
||||
}
|
||||
case runtimemetrics.KindFloat64Histogram:
|
||||
h := sample.Value.Float64Histogram()
|
||||
writeRuntimeHistogramMetric(w, name, h)
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected metric kind=%d", kind))
|
||||
}
|
||||
}
|
||||
|
||||
func writeRuntimeHistogramMetric(w io.Writer, name string, h *runtimemetrics.Float64Histogram) {
|
||||
buckets := h.Buckets
|
||||
counts := h.Counts
|
||||
if len(buckets) != len(counts)+1 {
|
||||
panic(fmt.Errorf("the number of buckets must be bigger than the number of counts by 1 in histogram %s; got buckets=%d, counts=%d", name, len(buckets), len(counts)))
|
||||
}
|
||||
tailCount := uint64(0)
|
||||
if strings.HasSuffix(name, "_seconds") {
|
||||
// Limit the maximum bucket to 1 second, since Go runtime exposes buckets with 10K seconds,
|
||||
// which have little sense. At the same time such buckets may lead to high cardinality issues
|
||||
// at the scraper side.
|
||||
for len(buckets) > 0 && buckets[len(buckets)-1] > 1 {
|
||||
buckets = buckets[:len(buckets)-1]
|
||||
tailCount += counts[len(counts)-1]
|
||||
counts = counts[:len(counts)-1]
|
||||
}
|
||||
}
|
||||
|
||||
iStep := float64(len(buckets)) / maxRuntimeHistogramBuckets
|
||||
|
||||
totalCount := uint64(0)
|
||||
iNext := 0.0
|
||||
WriteMetadataIfNeeded(w, name, "histogram")
|
||||
for i, count := range counts {
|
||||
totalCount += count
|
||||
if float64(i) >= iNext {
|
||||
iNext += iStep
|
||||
le := buckets[i+1]
|
||||
if !math.IsInf(le, 1) {
|
||||
fmt.Fprintf(w, `%s_bucket{le="%g"} %d`+"\n", name, le, totalCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
totalCount += tailCount
|
||||
fmt.Fprintf(w, `%s_bucket{le="+Inf"} %d`+"\n", name, totalCount)
|
||||
// _sum and _count are not exposed because the Go runtime histogram lacks accurate sum data.
|
||||
// Estimating the sum (as Prometheus does) could be misleading, while exposing only `_count` without `_sum` is impractical.
|
||||
// We can reconsider if precise sum data becomes available.
|
||||
//
|
||||
// References:
|
||||
// - Go runtime histogram: https://github.com/golang/go/blob/3432c68467d50ffc622fed230a37cd401d82d4bf/src/runtime/metrics/histogram.go#L8
|
||||
// - Prometheus estimate: https://github.com/prometheus/client_golang/blob/5fe1d33cea76068edd4ece5f58e52f81d225b13c/prometheus/go_collector_latest.go#L498
|
||||
// - Related discussion: https://github.com/VictoriaMetrics/metrics/issues/94
|
||||
}
|
||||
|
||||
// Limit the number of buckets for Go runtime histograms in order to prevent from high cardinality issues at scraper side.
|
||||
const maxRuntimeHistogramBuckets = 30
|
||||
270
vendor/github.com/VictoriaMetrics/metrics/histogram.go
generated
vendored
Normal file
270
vendor/github.com/VictoriaMetrics/metrics/histogram.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
e10Min = -9
|
||||
e10Max = 18
|
||||
bucketsPerDecimal = 18
|
||||
decimalBucketsCount = e10Max - e10Min
|
||||
bucketsCount = decimalBucketsCount * bucketsPerDecimal
|
||||
)
|
||||
|
||||
var bucketMultiplier = math.Pow(10, 1.0/bucketsPerDecimal)
|
||||
|
||||
// Histogram is a histogram for non-negative values with automatically created buckets.
|
||||
//
|
||||
// See https://medium.com/@valyala/improving-histogram-usability-for-prometheus-and-grafana-bc7e5df0e350
|
||||
//
|
||||
// Each bucket contains a counter for values in the given range.
|
||||
// Each non-empty bucket is exposed via the following metric:
|
||||
//
|
||||
// <metric_name>_bucket{<optional_tags>,vmrange="<start>...<end>"} <counter>
|
||||
//
|
||||
// Where:
|
||||
//
|
||||
// - <metric_name> is the metric name passed to NewHistogram
|
||||
// - <optional_tags> is optional tags for the <metric_name>, which are passed to NewHistogram
|
||||
// - <start> and <end> - start and end values for the given bucket
|
||||
// - <counter> - the number of hits to the given bucket during Update* calls
|
||||
//
|
||||
// Histogram buckets can be converted to Prometheus-like buckets with `le` labels
|
||||
// with `prometheus_buckets(<metric_name>_bucket)` function from PromQL extensions in VictoriaMetrics.
|
||||
// (see https://docs.victoriametrics.com/victoriametrics/metricsql/ ):
|
||||
//
|
||||
// prometheus_buckets(request_duration_bucket)
|
||||
//
|
||||
// Time series produced by the Histogram have better compression ratio comparing to
|
||||
// Prometheus histogram buckets with `le` labels, since they don't include counters
|
||||
// for all the previous buckets.
|
||||
//
|
||||
// Zero histogram is usable.
|
||||
type Histogram struct {
|
||||
// Mu guarantees synchronous update for all the counters and sum.
|
||||
//
|
||||
// Do not use sync.RWMutex, since it has zero sense from performance PoV.
|
||||
// It only complicates the code.
|
||||
mu sync.Mutex
|
||||
|
||||
// decimalBuckets contains counters for histogram buckets
|
||||
decimalBuckets [decimalBucketsCount]*[bucketsPerDecimal]uint64
|
||||
|
||||
// lower is the number of values, which hit the lower bucket
|
||||
lower uint64
|
||||
|
||||
// upper is the number of values, which hit the upper bucket
|
||||
upper uint64
|
||||
|
||||
// sum is the sum of all the values put into Histogram
|
||||
sum float64
|
||||
}
|
||||
|
||||
// Reset resets the given histogram.
|
||||
func (h *Histogram) Reset() {
|
||||
h.mu.Lock()
|
||||
for _, db := range h.decimalBuckets[:] {
|
||||
if db == nil {
|
||||
continue
|
||||
}
|
||||
for i := range db[:] {
|
||||
db[i] = 0
|
||||
}
|
||||
}
|
||||
h.lower = 0
|
||||
h.upper = 0
|
||||
h.sum = 0
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// Update updates h with v.
|
||||
//
|
||||
// Negative values and NaNs are ignored.
|
||||
func (h *Histogram) Update(v float64) {
|
||||
if math.IsNaN(v) || v < 0 {
|
||||
// Skip NaNs and negative values.
|
||||
return
|
||||
}
|
||||
bucketIdx := (math.Log10(v) - e10Min) * bucketsPerDecimal
|
||||
h.mu.Lock()
|
||||
h.sum += v
|
||||
if bucketIdx < 0 {
|
||||
h.lower++
|
||||
} else if bucketIdx >= bucketsCount {
|
||||
h.upper++
|
||||
} else {
|
||||
idx := uint(bucketIdx)
|
||||
if bucketIdx == float64(idx) && idx > 0 {
|
||||
// Edge case for 10^n values, which must go to the lower bucket
|
||||
// according to Prometheus logic for `le`-based histograms.
|
||||
idx--
|
||||
}
|
||||
decimalBucketIdx := idx / bucketsPerDecimal
|
||||
offset := idx % bucketsPerDecimal
|
||||
db := h.decimalBuckets[decimalBucketIdx]
|
||||
if db == nil {
|
||||
var b [bucketsPerDecimal]uint64
|
||||
db = &b
|
||||
h.decimalBuckets[decimalBucketIdx] = db
|
||||
}
|
||||
db[offset]++
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// Merge merges src to h
|
||||
func (h *Histogram) Merge(src *Histogram) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
src.mu.Lock()
|
||||
defer src.mu.Unlock()
|
||||
|
||||
h.lower += src.lower
|
||||
h.upper += src.upper
|
||||
h.sum += src.sum
|
||||
|
||||
for i, dbSrc := range src.decimalBuckets {
|
||||
if dbSrc == nil {
|
||||
continue
|
||||
}
|
||||
dbDst := h.decimalBuckets[i]
|
||||
if dbDst == nil {
|
||||
var b [bucketsPerDecimal]uint64
|
||||
dbDst = &b
|
||||
h.decimalBuckets[i] = dbDst
|
||||
}
|
||||
for j := range dbSrc {
|
||||
dbDst[j] += dbSrc[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VisitNonZeroBuckets calls f for all buckets with non-zero counters.
|
||||
//
|
||||
// vmrange contains "<start>...<end>" string with bucket bounds. The lower bound
|
||||
// isn't included in the bucket, while the upper bound is included.
|
||||
// This is required to be compatible with Prometheus-style histogram buckets
|
||||
// with `le` (less or equal) labels.
|
||||
func (h *Histogram) VisitNonZeroBuckets(f func(vmrange string, count uint64)) {
|
||||
h.mu.Lock()
|
||||
if h.lower > 0 {
|
||||
f(lowerBucketRange, h.lower)
|
||||
}
|
||||
for decimalBucketIdx, db := range h.decimalBuckets[:] {
|
||||
if db == nil {
|
||||
continue
|
||||
}
|
||||
for offset, count := range db[:] {
|
||||
if count > 0 {
|
||||
bucketIdx := decimalBucketIdx*bucketsPerDecimal + offset
|
||||
vmrange := getVMRange(bucketIdx)
|
||||
f(vmrange, count)
|
||||
}
|
||||
}
|
||||
}
|
||||
if h.upper > 0 {
|
||||
f(upperBucketRange, h.upper)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// NewHistogram creates and returns new histogram with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func NewHistogram(name string) *Histogram {
|
||||
return defaultSet.NewHistogram(name)
|
||||
}
|
||||
|
||||
// GetOrCreateHistogram returns registered histogram with the given name
|
||||
// or creates new histogram if the registry doesn't contain histogram with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewHistogram instead of GetOrCreateHistogram.
|
||||
func GetOrCreateHistogram(name string) *Histogram {
|
||||
return defaultSet.GetOrCreateHistogram(name)
|
||||
}
|
||||
|
||||
// UpdateDuration updates request duration based on the given startTime.
|
||||
func (h *Histogram) UpdateDuration(startTime time.Time) {
|
||||
d := time.Since(startTime).Seconds()
|
||||
h.Update(d)
|
||||
}
|
||||
|
||||
func getVMRange(bucketIdx int) string {
|
||||
bucketRangesOnce.Do(initBucketRanges)
|
||||
return bucketRanges[bucketIdx]
|
||||
}
|
||||
|
||||
func initBucketRanges() {
|
||||
v := math.Pow10(e10Min)
|
||||
start := fmt.Sprintf("%.3e", v)
|
||||
for i := 0; i < bucketsCount; i++ {
|
||||
v *= bucketMultiplier
|
||||
end := fmt.Sprintf("%.3e", v)
|
||||
bucketRanges[i] = start + "..." + end
|
||||
start = end
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
lowerBucketRange = fmt.Sprintf("0...%.3e", math.Pow10(e10Min))
|
||||
upperBucketRange = fmt.Sprintf("%.3e...+Inf", math.Pow10(e10Max))
|
||||
|
||||
bucketRanges [bucketsCount]string
|
||||
bucketRangesOnce sync.Once
|
||||
)
|
||||
|
||||
func (h *Histogram) marshalTo(prefix string, w io.Writer) {
|
||||
countTotal := uint64(0)
|
||||
h.VisitNonZeroBuckets(func(vmrange string, count uint64) {
|
||||
tag := fmt.Sprintf("vmrange=%q", vmrange)
|
||||
metricName := addTag(prefix, tag)
|
||||
name, labels := splitMetricName(metricName)
|
||||
fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, count)
|
||||
countTotal += count
|
||||
})
|
||||
if countTotal == 0 {
|
||||
return
|
||||
}
|
||||
name, labels := splitMetricName(prefix)
|
||||
sum := h.getSum()
|
||||
if float64(int64(sum)) == sum {
|
||||
fmt.Fprintf(w, "%s_sum%s %d\n", name, labels, int64(sum))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s_sum%s %g\n", name, labels, sum)
|
||||
}
|
||||
fmt.Fprintf(w, "%s_count%s %d\n", name, labels, countTotal)
|
||||
}
|
||||
|
||||
func (h *Histogram) getSum() float64 {
|
||||
h.mu.Lock()
|
||||
sum := h.sum
|
||||
h.mu.Unlock()
|
||||
return sum
|
||||
}
|
||||
|
||||
func (h *Histogram) metricType() string {
|
||||
return "histogram"
|
||||
}
|
||||
355
vendor/github.com/VictoriaMetrics/metrics/metrics.go
generated
vendored
Normal file
355
vendor/github.com/VictoriaMetrics/metrics/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
// Package metrics implements Prometheus-compatible metrics for applications.
|
||||
//
|
||||
// This package is lightweight alternative to https://github.com/prometheus/client_golang
|
||||
// with simpler API and smaller dependencies.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// 1. Register the required metrics via New* functions.
|
||||
// 2. Expose them to `/metrics` page via WritePrometheus.
|
||||
// 3. Update the registered metrics during application lifetime.
|
||||
//
|
||||
// The package has been extracted from https://victoriametrics.com/
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type namedMetric struct {
|
||||
name string
|
||||
metric metric
|
||||
isAux bool
|
||||
}
|
||||
|
||||
type metric interface {
|
||||
marshalTo(prefix string, w io.Writer)
|
||||
metricType() string
|
||||
}
|
||||
|
||||
var defaultSet = NewSet()
|
||||
|
||||
func init() {
|
||||
RegisterSet(defaultSet)
|
||||
}
|
||||
|
||||
var (
|
||||
registeredSets = make(map[*Set]struct{})
|
||||
registeredSetsLock sync.Mutex
|
||||
)
|
||||
|
||||
// RegisterSet registers the given set s for metrics export via global WritePrometheus() call.
|
||||
//
|
||||
// See also UnregisterSet.
|
||||
func RegisterSet(s *Set) {
|
||||
registeredSetsLock.Lock()
|
||||
registeredSets[s] = struct{}{}
|
||||
registeredSetsLock.Unlock()
|
||||
}
|
||||
|
||||
// UnregisterSet stops exporting metrics for the given s via global WritePrometheus() call.
|
||||
//
|
||||
// If destroySet is set to true, then s.UnregisterAllMetrics() is called on s after unregistering it,
|
||||
// so s becomes destroyed. Otherwise the s can be registered again in the set by passing it to RegisterSet().
|
||||
func UnregisterSet(s *Set, destroySet bool) {
|
||||
registeredSetsLock.Lock()
|
||||
delete(registeredSets, s)
|
||||
registeredSetsLock.Unlock()
|
||||
|
||||
if destroySet {
|
||||
s.UnregisterAllMetrics()
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterMetricsWriter registers writeMetrics callback for including metrics in the output generated by WritePrometheus.
|
||||
//
|
||||
// The writeMetrics callback must write metrics to w in Prometheus text exposition format without timestamps and trailing comments.
|
||||
// The last line generated by writeMetrics must end with \n.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is OK to register multiple writeMetrics callbacks - all of them will be called sequentially for gererating the output at WritePrometheus.
|
||||
func RegisterMetricsWriter(writeMetrics func(w io.Writer)) {
|
||||
defaultSet.RegisterMetricsWriter(writeMetrics)
|
||||
}
|
||||
|
||||
// WritePrometheus writes all the metrics in Prometheus format from the default set, all the added sets and metrics writers to w.
|
||||
//
|
||||
// Additional sets can be registered via RegisterSet() call.
|
||||
// Additional metric writers can be registered via RegisterMetricsWriter() call.
|
||||
//
|
||||
// If exposeProcessMetrics is true, then various `go_*` and `process_*` metrics
|
||||
// are exposed for the current process.
|
||||
//
|
||||
// The WritePrometheus func is usually called inside "/metrics" handler:
|
||||
//
|
||||
// http.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
|
||||
// metrics.WritePrometheus(w, true)
|
||||
// })
|
||||
func WritePrometheus(w io.Writer, exposeProcessMetrics bool) {
|
||||
registeredSetsLock.Lock()
|
||||
sets := make([]*Set, 0, len(registeredSets))
|
||||
for s := range registeredSets {
|
||||
sets = append(sets, s)
|
||||
}
|
||||
registeredSetsLock.Unlock()
|
||||
|
||||
sort.Slice(sets, func(i, j int) bool {
|
||||
return uintptr(unsafe.Pointer(sets[i])) < uintptr(unsafe.Pointer(sets[j]))
|
||||
})
|
||||
for _, s := range sets {
|
||||
s.WritePrometheus(w)
|
||||
}
|
||||
if exposeProcessMetrics {
|
||||
WriteProcessMetrics(w)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteProcessMetrics writes additional process metrics in Prometheus format to w.
|
||||
//
|
||||
// The following `go_*` and `process_*` metrics are exposed for the currently
|
||||
// running process. Below is a short description for the exposed `process_*` metrics:
|
||||
//
|
||||
// - process_cpu_seconds_system_total - CPU time spent in syscalls
|
||||
//
|
||||
// - process_cpu_seconds_user_total - CPU time spent in userspace
|
||||
//
|
||||
// - process_cpu_seconds_total - CPU time spent by the process
|
||||
//
|
||||
// - process_major_pagefaults_total - page faults resulted in disk IO
|
||||
//
|
||||
// - process_minor_pagefaults_total - page faults resolved without disk IO
|
||||
//
|
||||
// - process_resident_memory_bytes - recently accessed memory (aka RSS or resident memory)
|
||||
//
|
||||
// - process_resident_memory_peak_bytes - the maximum RSS memory usage
|
||||
//
|
||||
// - process_resident_memory_anon_bytes - RSS for memory-mapped files
|
||||
//
|
||||
// - process_resident_memory_file_bytes - RSS for memory allocated by the process
|
||||
//
|
||||
// - process_resident_memory_shared_bytes - RSS for memory shared between multiple processes
|
||||
//
|
||||
// - process_virtual_memory_bytes - virtual memory usage
|
||||
//
|
||||
// - process_virtual_memory_peak_bytes - the maximum virtual memory usage
|
||||
//
|
||||
// - process_num_threads - the number of threads
|
||||
//
|
||||
// - process_start_time_seconds - process start time as unix timestamp
|
||||
//
|
||||
// - process_io_read_bytes_total - the number of bytes read via syscalls
|
||||
//
|
||||
// - process_io_written_bytes_total - the number of bytes written via syscalls
|
||||
//
|
||||
// - process_io_read_syscalls_total - the number of read syscalls
|
||||
//
|
||||
// - process_io_write_syscalls_total - the number of write syscalls
|
||||
//
|
||||
// - process_io_storage_read_bytes_total - the number of bytes actually read from disk
|
||||
//
|
||||
// - process_io_storage_written_bytes_total - the number of bytes actually written to disk
|
||||
//
|
||||
// - process_pressure_cpu_waiting_seconds_total - the number of seconds processes in the current cgroup v2 were waiting to be executed
|
||||
//
|
||||
// - process_pressure_cpu_stalled_seconds_total - the number of seconds all the processes in the current cgroup v2 were stalled
|
||||
//
|
||||
// - process_pressure_io_waiting_seconds_total - the number of seconds processes in the current cgroup v2 were waiting for io to complete
|
||||
//
|
||||
// - process_pressure_io_stalled_seconds_total - the number of seconds all the processes in the current cgroup v2 were waiting for io to complete
|
||||
//
|
||||
// - process_pressure_memory_waiting_seconds_total - the number of seconds processes in the current cgroup v2 were waiting for memory access to complete
|
||||
//
|
||||
// - process_pressure_memory_stalled_seconds_total - the number of seconds all the processes in the current cgroup v2 were waiting for memory access to complete
|
||||
//
|
||||
// - go_sched_latencies_seconds - time spent by goroutines in ready state before they start execution
|
||||
//
|
||||
// - go_mutex_wait_seconds_total - summary time spent by all the goroutines while waiting for locked mutex
|
||||
//
|
||||
// - go_gc_mark_assist_cpu_seconds_total - summary CPU time spent by goroutines in GC mark assist state
|
||||
//
|
||||
// - go_gc_cpu_seconds_total - summary time spent in GC
|
||||
//
|
||||
// - go_gc_pauses_seconds - duration of GC pauses
|
||||
//
|
||||
// - go_scavenge_cpu_seconds_total - CPU time spent on returning the memory to OS
|
||||
//
|
||||
// - go_memlimit_bytes - the GOMEMLIMIT env var value
|
||||
//
|
||||
// - go_memstats_alloc_bytes - memory usage for Go objects in the heap
|
||||
//
|
||||
// - go_memstats_alloc_bytes_total - the cumulative counter for total size of allocated Go objects
|
||||
//
|
||||
// - go_memstats_buck_hash_sys_bytes - bytes of memory in profiling bucket hash tables
|
||||
//
|
||||
// - go_memstats_frees_total - the cumulative counter for number of freed Go objects
|
||||
//
|
||||
// - go_memstats_gc_cpu_fraction - the fraction of CPU spent in Go garbage collector
|
||||
//
|
||||
// - go_memstats_gc_sys_bytes - the size of Go garbage collector metadata
|
||||
//
|
||||
// - go_memstats_heap_alloc_bytes - the same as go_memstats_alloc_bytes
|
||||
//
|
||||
// - go_memstats_heap_idle_bytes - idle memory ready for new Go object allocations
|
||||
//
|
||||
// - go_memstats_heap_inuse_bytes - bytes in in-use spans
|
||||
//
|
||||
// - go_memstats_heap_objects - the number of Go objects in the heap
|
||||
//
|
||||
// - go_memstats_heap_released_bytes - bytes of physical memory returned to the OS
|
||||
//
|
||||
// - go_memstats_heap_sys_bytes - memory requested for Go objects from the OS
|
||||
//
|
||||
// - go_memstats_last_gc_time_seconds - unix timestamp the last garbage collection finished
|
||||
//
|
||||
// - go_memstats_lookups_total - the number of pointer lookups performed by the runtime
|
||||
//
|
||||
// - go_memstats_mallocs_total - the number of allocations for Go objects
|
||||
//
|
||||
// - go_memstats_mcache_inuse_bytes - bytes of allocated mcache structures
|
||||
//
|
||||
// - go_memstats_mcache_sys_bytes - bytes of memory obtained from the OS for mcache structures
|
||||
//
|
||||
// - go_memstats_mspan_inuse_bytes - bytes of allocated mspan structures
|
||||
//
|
||||
// - go_memstats_mspan_sys_bytes - bytes of memory obtained from the OS for mspan structures
|
||||
//
|
||||
// - go_memstats_next_gc_bytes - the target heap size when the next garbage collection should start
|
||||
//
|
||||
// - go_memstats_other_sys_bytes - bytes of memory in miscellaneous off-heap runtime allocations
|
||||
//
|
||||
// - go_memstats_stack_inuse_bytes - memory used for goroutine stacks
|
||||
//
|
||||
// - go_memstats_stack_sys_bytes - memory requested fromthe OS for goroutine stacks
|
||||
//
|
||||
// - go_memstats_sys_bytes - memory requested by Go runtime from the OS
|
||||
//
|
||||
// - go_cgo_calls_count - the total number of CGO calls
|
||||
//
|
||||
// - go_cpu_count - the number of CPU cores on the host where the app runs
|
||||
//
|
||||
// The WriteProcessMetrics func is usually called in combination with writing Set metrics
|
||||
// inside "/metrics" handler:
|
||||
//
|
||||
// http.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
|
||||
// mySet.WritePrometheus(w)
|
||||
// metrics.WriteProcessMetrics(w)
|
||||
// })
|
||||
//
|
||||
// See also WriteFDMetrics.
|
||||
func WriteProcessMetrics(w io.Writer) {
|
||||
writeGoMetrics(w)
|
||||
writeProcessMetrics(w)
|
||||
writePushMetrics(w)
|
||||
}
|
||||
|
||||
// WriteFDMetrics writes `process_max_fds` and `process_open_fds` metrics to w.
|
||||
func WriteFDMetrics(w io.Writer) {
|
||||
writeFDMetrics(w)
|
||||
}
|
||||
|
||||
// UnregisterMetric removes metric with the given name from default set.
|
||||
//
|
||||
// See also UnregisterAllMetrics.
|
||||
func UnregisterMetric(name string) bool {
|
||||
return defaultSet.UnregisterMetric(name)
|
||||
}
|
||||
|
||||
// UnregisterAllMetrics unregisters all the metrics from default set.
|
||||
//
|
||||
// It also unregisters writeMetrics callbacks passed to RegisterMetricsWriter.
|
||||
func UnregisterAllMetrics() {
|
||||
defaultSet.UnregisterAllMetrics()
|
||||
}
|
||||
|
||||
// ListMetricNames returns sorted list of all the metric names from default set.
|
||||
func ListMetricNames() []string {
|
||||
return defaultSet.ListMetricNames()
|
||||
}
|
||||
|
||||
// GetDefaultSet returns the default metrics set.
|
||||
func GetDefaultSet() *Set {
|
||||
return defaultSet
|
||||
}
|
||||
|
||||
// ExposeMetadata allows enabling adding TYPE and HELP metadata to the exposed metrics globally.
|
||||
//
|
||||
// It is safe to call this method multiple times. It is allowed to change it in runtime.
|
||||
// ExposeMetadata is set to false by default.
|
||||
func ExposeMetadata(v bool) {
|
||||
n := 0
|
||||
if v {
|
||||
n = 1
|
||||
}
|
||||
atomic.StoreUint32(&exposeMetadata, uint32(n))
|
||||
}
|
||||
|
||||
func isMetadataEnabled() bool {
|
||||
n := atomic.LoadUint32(&exposeMetadata)
|
||||
return n != 0
|
||||
}
|
||||
|
||||
var exposeMetadata uint32
|
||||
|
||||
func isCounterName(name string) bool {
|
||||
return strings.HasSuffix(name, "_total")
|
||||
}
|
||||
|
||||
// WriteGaugeUint64 writes gauge metric with the given name and value to w in Prometheus text exposition format.
|
||||
func WriteGaugeUint64(w io.Writer, name string, value uint64) {
|
||||
writeMetricUint64(w, name, "gauge", value)
|
||||
}
|
||||
|
||||
// WriteGaugeFloat64 writes gauge metric with the given name and value to w in Prometheus text exposition format.
|
||||
func WriteGaugeFloat64(w io.Writer, name string, value float64) {
|
||||
writeMetricFloat64(w, name, "gauge", value)
|
||||
}
|
||||
|
||||
// WriteCounterUint64 writes counter metric with the given name and value to w in Prometheus text exposition format.
|
||||
func WriteCounterUint64(w io.Writer, name string, value uint64) {
|
||||
writeMetricUint64(w, name, "counter", value)
|
||||
}
|
||||
|
||||
// WriteCounterFloat64 writes counter metric with the given name and value to w in Prometheus text exposition format.
|
||||
func WriteCounterFloat64(w io.Writer, name string, value float64) {
|
||||
writeMetricFloat64(w, name, "counter", value)
|
||||
}
|
||||
|
||||
func writeMetricUint64(w io.Writer, metricName, metricType string, value uint64) {
|
||||
WriteMetadataIfNeeded(w, metricName, metricType)
|
||||
fmt.Fprintf(w, "%s %d\n", metricName, value)
|
||||
}
|
||||
|
||||
func writeMetricFloat64(w io.Writer, metricName, metricType string, value float64) {
|
||||
WriteMetadataIfNeeded(w, metricName, metricType)
|
||||
fmt.Fprintf(w, "%s %g\n", metricName, value)
|
||||
}
|
||||
|
||||
// WriteMetadataIfNeeded writes HELP and TYPE metadata for the given metricName and metricType if this is globally enabled via ExposeMetadata().
|
||||
//
|
||||
// If the metadata exposition isn't enabled, then this function is no-op.
|
||||
func WriteMetadataIfNeeded(w io.Writer, metricName, metricType string) {
|
||||
if !isMetadataEnabled() {
|
||||
return
|
||||
}
|
||||
metricFamily := getMetricFamily(metricName)
|
||||
writeMetadata(w, metricFamily, metricType)
|
||||
}
|
||||
|
||||
func writeMetadata(w io.Writer, metricFamily, metricType string) {
|
||||
fmt.Fprintf(w, "# HELP %s\n", metricFamily)
|
||||
fmt.Fprintf(w, "# TYPE %s %s\n", metricFamily, metricType)
|
||||
}
|
||||
|
||||
func getMetricFamily(metricName string) string {
|
||||
n := strings.IndexByte(metricName, '{')
|
||||
if n < 0 {
|
||||
return metricName
|
||||
}
|
||||
return metricName[:n]
|
||||
}
|
||||
419
vendor/github.com/VictoriaMetrics/metrics/process_metrics_linux.go
generated
vendored
Normal file
419
vendor/github.com/VictoriaMetrics/metrics/process_metrics_linux.go
generated
vendored
Normal file
@@ -0,0 +1,419 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// See https://github.com/prometheus/procfs/blob/a4ac0826abceb44c40fc71daed2b301db498b93e/proc_stat.go#L40 .
|
||||
const userHZ = 100
|
||||
|
||||
// Different environments may have different page size.
|
||||
//
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6457
|
||||
var pageSizeBytes = uint64(os.Getpagesize())
|
||||
|
||||
// See http://man7.org/linux/man-pages/man5/proc.5.html
|
||||
type procStat struct {
|
||||
State byte
|
||||
Ppid int
|
||||
Pgrp int
|
||||
Session int
|
||||
TtyNr int
|
||||
Tpgid int
|
||||
Flags uint
|
||||
Minflt uint
|
||||
Cminflt uint
|
||||
Majflt uint
|
||||
Cmajflt uint
|
||||
Utime uint
|
||||
Stime uint
|
||||
Cutime int
|
||||
Cstime int
|
||||
Priority int
|
||||
Nice int
|
||||
NumThreads int
|
||||
ItrealValue int
|
||||
Starttime uint64
|
||||
Vsize uint
|
||||
Rss int
|
||||
}
|
||||
|
||||
func writeProcessMetrics(w io.Writer) {
|
||||
statFilepath := "/proc/self/stat"
|
||||
data, err := ioutil.ReadFile(statFilepath)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot open %s: %s", statFilepath, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Search for the end of command.
|
||||
n := bytes.LastIndex(data, []byte(") "))
|
||||
if n < 0 {
|
||||
log.Printf("ERROR: metrics: cannot find command in parentheses in %q read from %s", data, statFilepath)
|
||||
return
|
||||
}
|
||||
data = data[n+2:]
|
||||
|
||||
var p procStat
|
||||
bb := bytes.NewBuffer(data)
|
||||
_, err = fmt.Fscanf(bb, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
|
||||
&p.State, &p.Ppid, &p.Pgrp, &p.Session, &p.TtyNr, &p.Tpgid, &p.Flags, &p.Minflt, &p.Cminflt, &p.Majflt, &p.Cmajflt,
|
||||
&p.Utime, &p.Stime, &p.Cutime, &p.Cstime, &p.Priority, &p.Nice, &p.NumThreads, &p.ItrealValue, &p.Starttime, &p.Vsize, &p.Rss)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot parse %q read from %s: %s", data, statFilepath, err)
|
||||
return
|
||||
}
|
||||
|
||||
// It is expensive obtaining `process_open_fds` when big number of file descriptors is opened,
|
||||
// so don't do it here.
|
||||
// See writeFDMetrics instead.
|
||||
|
||||
utime := float64(p.Utime) / userHZ
|
||||
stime := float64(p.Stime) / userHZ
|
||||
|
||||
// Calculate totalTime by dividing the sum of p.Utime and p.Stime by userHZ.
|
||||
// This reduces possible floating-point precision loss
|
||||
totalTime := float64(p.Utime+p.Stime) / userHZ
|
||||
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_system_total", stime)
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_total", totalTime)
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_user_total", utime)
|
||||
WriteCounterUint64(w, "process_major_pagefaults_total", uint64(p.Majflt))
|
||||
WriteCounterUint64(w, "process_minor_pagefaults_total", uint64(p.Minflt))
|
||||
WriteGaugeUint64(w, "process_num_threads", uint64(p.NumThreads))
|
||||
WriteGaugeUint64(w, "process_resident_memory_bytes", uint64(p.Rss)*pageSizeBytes)
|
||||
WriteGaugeUint64(w, "process_start_time_seconds", uint64(startTimeSeconds))
|
||||
WriteGaugeUint64(w, "process_virtual_memory_bytes", uint64(p.Vsize))
|
||||
writeProcessMemMetrics(w)
|
||||
writeIOMetrics(w)
|
||||
writePSIMetrics(w)
|
||||
}
|
||||
|
||||
var procSelfIOErrLogged uint32
|
||||
|
||||
func writeIOMetrics(w io.Writer) {
|
||||
ioFilepath := "/proc/self/io"
|
||||
data, err := ioutil.ReadFile(ioFilepath)
|
||||
if err != nil {
|
||||
// Do not spam the logs with errors - this error cannot be fixed without process restart.
|
||||
// See https://github.com/VictoriaMetrics/metrics/issues/42
|
||||
if atomic.CompareAndSwapUint32(&procSelfIOErrLogged, 0, 1) {
|
||||
log.Printf("ERROR: metrics: cannot read process_io_* metrics from %q, so these metrics won't be updated until the error is fixed; "+
|
||||
"see https://github.com/VictoriaMetrics/metrics/issues/42 ; The error: %s", ioFilepath, err)
|
||||
}
|
||||
}
|
||||
|
||||
getInt := func(s string) int64 {
|
||||
n := strings.IndexByte(s, ' ')
|
||||
if n < 0 {
|
||||
log.Printf("ERROR: metrics: cannot find whitespace in %q at %q", s, ioFilepath)
|
||||
return 0
|
||||
}
|
||||
v, err := strconv.ParseInt(s[n+1:], 10, 64)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot parse %q at %q: %s", s, ioFilepath, err)
|
||||
return 0
|
||||
}
|
||||
return v
|
||||
}
|
||||
var rchar, wchar, syscr, syscw, readBytes, writeBytes int64
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, s := range lines {
|
||||
s = strings.TrimSpace(s)
|
||||
switch {
|
||||
case strings.HasPrefix(s, "rchar: "):
|
||||
rchar = getInt(s)
|
||||
case strings.HasPrefix(s, "wchar: "):
|
||||
wchar = getInt(s)
|
||||
case strings.HasPrefix(s, "syscr: "):
|
||||
syscr = getInt(s)
|
||||
case strings.HasPrefix(s, "syscw: "):
|
||||
syscw = getInt(s)
|
||||
case strings.HasPrefix(s, "read_bytes: "):
|
||||
readBytes = getInt(s)
|
||||
case strings.HasPrefix(s, "write_bytes: "):
|
||||
writeBytes = getInt(s)
|
||||
}
|
||||
}
|
||||
WriteGaugeUint64(w, "process_io_read_bytes_total", uint64(rchar))
|
||||
WriteGaugeUint64(w, "process_io_written_bytes_total", uint64(wchar))
|
||||
WriteGaugeUint64(w, "process_io_read_syscalls_total", uint64(syscr))
|
||||
WriteGaugeUint64(w, "process_io_write_syscalls_total", uint64(syscw))
|
||||
WriteGaugeUint64(w, "process_io_storage_read_bytes_total", uint64(readBytes))
|
||||
WriteGaugeUint64(w, "process_io_storage_written_bytes_total", uint64(writeBytes))
|
||||
}
|
||||
|
||||
var startTimeSeconds = time.Now().Unix()
|
||||
|
||||
// writeFDMetrics writes process_max_fds and process_open_fds metrics to w.
|
||||
func writeFDMetrics(w io.Writer) {
|
||||
totalOpenFDs, err := getOpenFDsCount("/proc/self/fd")
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot determine open file descriptors count: %s", err)
|
||||
return
|
||||
}
|
||||
maxOpenFDs, err := getMaxFilesLimit("/proc/self/limits")
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot determine the limit on open file descritors: %s", err)
|
||||
return
|
||||
}
|
||||
WriteGaugeUint64(w, "process_max_fds", maxOpenFDs)
|
||||
WriteGaugeUint64(w, "process_open_fds", totalOpenFDs)
|
||||
}
|
||||
|
||||
func getOpenFDsCount(path string) (uint64, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
var totalOpenFDs uint64
|
||||
for {
|
||||
names, err := f.Readdirnames(512)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unexpected error at Readdirnames: %s", err)
|
||||
}
|
||||
totalOpenFDs += uint64(len(names))
|
||||
}
|
||||
return totalOpenFDs, nil
|
||||
}
|
||||
|
||||
func getMaxFilesLimit(path string) (uint64, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lines := strings.Split(string(data), "\n")
|
||||
const prefix = "Max open files"
|
||||
for _, s := range lines {
|
||||
if !strings.HasPrefix(s, prefix) {
|
||||
continue
|
||||
}
|
||||
text := strings.TrimSpace(s[len(prefix):])
|
||||
// Extract soft limit.
|
||||
n := strings.IndexByte(text, ' ')
|
||||
if n < 0 {
|
||||
return 0, fmt.Errorf("cannot extract soft limit from %q", s)
|
||||
}
|
||||
text = text[:n]
|
||||
if text == "unlimited" {
|
||||
return 1<<64 - 1, nil
|
||||
}
|
||||
limit, err := strconv.ParseUint(text, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot parse soft limit from %q: %s", s, err)
|
||||
}
|
||||
return limit, nil
|
||||
}
|
||||
return 0, fmt.Errorf("cannot find max open files limit")
|
||||
}
|
||||
|
||||
// https://man7.org/linux/man-pages/man5/procfs.5.html
|
||||
type memStats struct {
|
||||
vmPeak uint64
|
||||
rssPeak uint64
|
||||
rssAnon uint64
|
||||
rssFile uint64
|
||||
rssShmem uint64
|
||||
}
|
||||
|
||||
func writeProcessMemMetrics(w io.Writer) {
|
||||
ms, err := getMemStats("/proc/self/status")
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot determine memory status: %s", err)
|
||||
return
|
||||
}
|
||||
WriteGaugeUint64(w, "process_virtual_memory_peak_bytes", ms.vmPeak)
|
||||
WriteGaugeUint64(w, "process_resident_memory_peak_bytes", ms.rssPeak)
|
||||
WriteGaugeUint64(w, "process_resident_memory_anon_bytes", ms.rssAnon)
|
||||
WriteGaugeUint64(w, "process_resident_memory_file_bytes", ms.rssFile)
|
||||
WriteGaugeUint64(w, "process_resident_memory_shared_bytes", ms.rssShmem)
|
||||
}
|
||||
|
||||
func getMemStats(path string) (*memStats, error) {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ms memStats
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, s := range lines {
|
||||
if !strings.HasPrefix(s, "Vm") && !strings.HasPrefix(s, "Rss") {
|
||||
continue
|
||||
}
|
||||
// Extract key value.
|
||||
line := strings.Fields(s)
|
||||
if len(line) != 3 {
|
||||
return nil, fmt.Errorf("unexpected number of fields found in %q; got %d; want %d", s, len(line), 3)
|
||||
}
|
||||
memStatName := line[0]
|
||||
memStatValue := line[1]
|
||||
value, err := strconv.ParseUint(memStatValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse number from %q: %w", s, err)
|
||||
}
|
||||
if line[2] != "kB" {
|
||||
return nil, fmt.Errorf("expecting kB value in %q; got %q", s, line[2])
|
||||
}
|
||||
value *= 1024
|
||||
switch memStatName {
|
||||
case "VmPeak:":
|
||||
ms.vmPeak = value
|
||||
case "VmHWM:":
|
||||
ms.rssPeak = value
|
||||
case "RssAnon:":
|
||||
ms.rssAnon = value
|
||||
case "RssFile:":
|
||||
ms.rssFile = value
|
||||
case "RssShmem:":
|
||||
ms.rssShmem = value
|
||||
}
|
||||
}
|
||||
return &ms, nil
|
||||
}
|
||||
|
||||
// writePSIMetrics writes PSI total metrics for the current process to w.
|
||||
//
|
||||
// See https://docs.kernel.org/accounting/psi.html
|
||||
func writePSIMetrics(w io.Writer) {
|
||||
if psiMetricsStart == nil {
|
||||
// Failed to initialize PSI metrics
|
||||
return
|
||||
}
|
||||
|
||||
m, err := getPSIMetrics()
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot expose PSI metrics: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
WriteCounterFloat64(w, "process_pressure_cpu_waiting_seconds_total", psiTotalSecs(m.cpuSome-psiMetricsStart.cpuSome))
|
||||
WriteCounterFloat64(w, "process_pressure_cpu_stalled_seconds_total", psiTotalSecs(m.cpuFull-psiMetricsStart.cpuFull))
|
||||
|
||||
WriteCounterFloat64(w, "process_pressure_io_waiting_seconds_total", psiTotalSecs(m.ioSome-psiMetricsStart.ioSome))
|
||||
WriteCounterFloat64(w, "process_pressure_io_stalled_seconds_total", psiTotalSecs(m.ioFull-psiMetricsStart.ioFull))
|
||||
|
||||
WriteCounterFloat64(w, "process_pressure_memory_waiting_seconds_total", psiTotalSecs(m.memSome-psiMetricsStart.memSome))
|
||||
WriteCounterFloat64(w, "process_pressure_memory_stalled_seconds_total", psiTotalSecs(m.memFull-psiMetricsStart.memFull))
|
||||
}
|
||||
|
||||
func psiTotalSecs(microsecs uint64) float64 {
|
||||
// PSI total stats is in microseconds according to https://docs.kernel.org/accounting/psi.html
|
||||
// Convert it to seconds.
|
||||
return float64(microsecs) / 1e6
|
||||
}
|
||||
|
||||
// psiMetricsStart contains the initial PSI metric values on program start.
|
||||
// it is needed in order to make sure the exposed PSI metrics start from zero.
|
||||
var psiMetricsStart = func() *psiMetrics {
|
||||
m, err := getPSIMetrics()
|
||||
if err != nil {
|
||||
log.Printf("INFO: metrics: disable exposing PSI metrics because of failed init: %s", err)
|
||||
return nil
|
||||
}
|
||||
return m
|
||||
}()
|
||||
|
||||
type psiMetrics struct {
|
||||
cpuSome uint64
|
||||
cpuFull uint64
|
||||
ioSome uint64
|
||||
ioFull uint64
|
||||
memSome uint64
|
||||
memFull uint64
|
||||
}
|
||||
|
||||
func getPSIMetrics() (*psiMetrics, error) {
|
||||
cgroupPath := getCgroupV2Path()
|
||||
if cgroupPath == "" {
|
||||
// Do nothing, since PSI requires cgroup v2, and the process doesn't run under cgroup v2.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cpuSome, cpuFull, err := readPSITotals(cgroupPath, "cpu.pressure")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ioSome, ioFull, err := readPSITotals(cgroupPath, "io.pressure")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
memSome, memFull, err := readPSITotals(cgroupPath, "memory.pressure")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := &psiMetrics{
|
||||
cpuSome: cpuSome,
|
||||
cpuFull: cpuFull,
|
||||
ioSome: ioSome,
|
||||
ioFull: ioFull,
|
||||
memSome: memSome,
|
||||
memFull: memFull,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func readPSITotals(cgroupPath, statsName string) (uint64, uint64, error) {
|
||||
filePath := cgroupPath + "/" + statsName
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(data), "\n")
|
||||
some := uint64(0)
|
||||
full := uint64(0)
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if !strings.HasPrefix(line, "some ") && !strings.HasPrefix(line, "full ") {
|
||||
continue
|
||||
}
|
||||
|
||||
tmp := strings.SplitN(line, "total=", 2)
|
||||
if len(tmp) != 2 {
|
||||
return 0, 0, fmt.Errorf("cannot find total from the line %q at %q", line, filePath)
|
||||
}
|
||||
microsecs, err := strconv.ParseUint(tmp[1], 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("cannot parse total=%q at %q: %w", tmp[1], filePath, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(line, "some "):
|
||||
some = microsecs
|
||||
case strings.HasPrefix(line, "full "):
|
||||
full = microsecs
|
||||
}
|
||||
}
|
||||
return some, full, nil
|
||||
}
|
||||
|
||||
func getCgroupV2Path() string {
|
||||
data, err := ioutil.ReadFile("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
tmp := strings.SplitN(string(data), "::", 2)
|
||||
if len(tmp) != 2 {
|
||||
return ""
|
||||
}
|
||||
path := "/sys/fs/cgroup" + strings.TrimSpace(tmp[1])
|
||||
|
||||
// Drop trailing slash if it exsits. This prevents from '//' in the constructed paths by the caller.
|
||||
return strings.TrimSuffix(path, "/")
|
||||
}
|
||||
15
vendor/github.com/VictoriaMetrics/metrics/process_metrics_other.go
generated
vendored
Normal file
15
vendor/github.com/VictoriaMetrics/metrics/process_metrics_other.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build !linux && !windows && !solaris
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func writeProcessMetrics(w io.Writer) {
|
||||
// TODO: implement it
|
||||
}
|
||||
|
||||
func writeFDMetrics(w io.Writer) {
|
||||
// TODO: implement it.
|
||||
}
|
||||
595
vendor/github.com/VictoriaMetrics/metrics/process_metrics_solaris.go
generated
vendored
Normal file
595
vendor/github.com/VictoriaMetrics/metrics/process_metrics_solaris.go
generated
vendored
Normal file
@@ -0,0 +1,595 @@
|
||||
//go:build solaris
|
||||
|
||||
// Author: Jens Elkner (C) 2025
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/** Solaris 11.3 types deduced from /usr/include/sys/procfs.h **/
|
||||
// requires go v1.18+
|
||||
type uchar_t uint8 // unsigned char
|
||||
type char int8 // signed char
|
||||
type short int16
|
||||
type ushort_t uint16
|
||||
type id_t int32
|
||||
type pid_t int32
|
||||
type uid_t uint32
|
||||
type gid_t uid_t
|
||||
type taskid_t id_t
|
||||
type projid_t id_t
|
||||
type zoneid_t id_t
|
||||
type poolid_t id_t
|
||||
type uintptr_t uint64
|
||||
type long int64
|
||||
type ulong_t uint64
|
||||
type dev_t ulong_t
|
||||
type size_t ulong_t
|
||||
type time_t long
|
||||
type sigset_t [16]char // we do not need those struct, so just pad
|
||||
type fltset_t [16]char // we do not need those struct, so just pad
|
||||
type sysset_t [64]char // we do not need those struct, so just pad
|
||||
type lwpstatus_t [1296]char // we do not need those struct, so just pad
|
||||
type lwpsinfo_t [152]char // we do not need those struct, so just pad
|
||||
|
||||
type timestruc_t struct {
|
||||
tv_sec time_t
|
||||
tv_nsec long
|
||||
}
|
||||
|
||||
/* process status file: /proc/<pid>/status */
|
||||
type pstatus_t struct {
|
||||
pr_flags int32 /* flags (see below) */
|
||||
pr_nlwp int32 /* number of active lwps in the process */
|
||||
pr_pid pid_t /* process id */
|
||||
pr_ppid pid_t /* parent process id */
|
||||
pr_pgid pid_t /* process group id */
|
||||
pr_sid pid_t /* session id */
|
||||
pr_aslwpid id_t /* historical; now always zero */
|
||||
pr_agentid id_t /* lwp id of the /proc agent lwp, if any */
|
||||
// 32
|
||||
pr_sigpend sigset_t /* set of process pending signals */
|
||||
pr_brkbase uintptr_t /* address of the process heap */
|
||||
pr_brksize size_t /* size of the process heap, in bytes */
|
||||
// 64
|
||||
pr_stkbase uintptr_t /* address of the process stack */
|
||||
pr_stksize size_t /* size of the process stack, in bytes */
|
||||
pr_utime timestruc_t /* # process user cpu time */
|
||||
// 96
|
||||
pr_stime timestruc_t /* # process system cpu time */
|
||||
pr_cutime timestruc_t /* # sum of children's user times */
|
||||
// 128
|
||||
pr_cstime timestruc_t /* # sum of children's system times */
|
||||
pr_sigtrace sigset_t /* sigset_t: set of traced signals */
|
||||
// 160
|
||||
pr_flttrace fltset_t /* set of traced faults */
|
||||
pr_sysentry sysset_t /* set of system calls traced on entry */
|
||||
// 240
|
||||
pr_sysexit sysset_t /* set of system calls traced on exit */
|
||||
// 304
|
||||
pr_dmodel char /* data model of the process (see below) */
|
||||
pr_va_mask uchar_t /* VA masking bits, where supported */
|
||||
pr_adi_nbits uchar_t /* # of VA bits used by ADI when enabled */
|
||||
pr_pad [1]char
|
||||
pr_taskid taskid_t /* task id */
|
||||
// 312
|
||||
pr_projid projid_t /* project id */
|
||||
pr_nzomb int32 /* number of zombie lwps in the process */
|
||||
pr_zoneid zoneid_t /* zone id */
|
||||
// 324
|
||||
pr_filler [15]int32 /* reserved for future use */
|
||||
// 384
|
||||
pr_lwp lwpstatus_t /* status of the representative lwp */
|
||||
// 1680
|
||||
}
|
||||
|
||||
const PRARGSZ = 80 /* number of chars of arguments */
|
||||
const PRFNSZ = 16 /* Maximum size of execed filename */
|
||||
|
||||
/* process ps(1) information file. /proc/<pid>/psinfo */
|
||||
type psinfo_t struct {
|
||||
pr_flag int32 /* process flags (DEPRECATED; do not use) */
|
||||
pr_nlwp int32 /* number of active lwps in the process */
|
||||
pr_pid pid_t /* unique process id */
|
||||
pr_ppid pid_t /* process id of parent */
|
||||
pr_pgid pid_t /* pid of process group leader */
|
||||
pr_sid pid_t /* session id */
|
||||
pr_uid uid_t /* real user id */
|
||||
pr_euid uid_t /* effective user id */
|
||||
// 32
|
||||
pr_gid gid_t /* real group id */
|
||||
pr_egid gid_t /* effective group id */
|
||||
pr_addr uintptr_t /* address of process */
|
||||
pr_size size_t /* size of process image in Kbytes */
|
||||
pr_rssize size_t /* resident set size in Kbytes */
|
||||
// 64
|
||||
pr_rssizepriv size_t /* resident set size of private mappings */
|
||||
pr_ttydev dev_t /* controlling tty device (or PRNODEV) */
|
||||
/* The following percent numbers are 16-bit binary */
|
||||
/* fractions [0 .. 1] with the binary point to the */
|
||||
/* right of the high-order bit (1.0 == 0x8000) */
|
||||
pr_pctcpu ushort_t /* % of recent cpu time used by all lwps */
|
||||
pr_pctmem ushort_t /* % of system memory used by process */
|
||||
pr_dummy int32 /* 8 byte alignment: GO doesn't do it automagically */
|
||||
// 84 + 4 = 88
|
||||
pr_start timestruc_t /* process start time, from the epoch */
|
||||
pr_time timestruc_t /* usr+sys cpu time for this process */
|
||||
pr_ctime timestruc_t /* usr+sys cpu time for reaped children */
|
||||
// 136
|
||||
pr_fname [PRFNSZ]char /* name of execed file */
|
||||
pr_psargs [PRARGSZ]char /* initial characters of arg list */
|
||||
// 232
|
||||
pr_wstat int32 /* if zombie, the wait() status */
|
||||
pr_argc int32 /* initial argument count */
|
||||
pr_argv uintptr_t /* address of initial argument vector */
|
||||
pr_envp uintptr_t /* address of initial environment vector */
|
||||
pr_dmodel char /* data model of the process */
|
||||
pr_pad2 [3]char
|
||||
pr_taskid taskid_t /* task id */
|
||||
// 264
|
||||
pr_projid projid_t /* project id */
|
||||
pr_nzomb int32 /* number of zombie lwps in the process */
|
||||
pr_poolid poolid_t /* pool id */
|
||||
pr_zoneid zoneid_t /* zone id */
|
||||
pr_contract id_t /* process contract */
|
||||
pr_filler [1]int32 /* reserved for future use */
|
||||
// 288
|
||||
pr_lwp lwpsinfo_t /* information for representative lwp */
|
||||
// 440
|
||||
}
|
||||
|
||||
/* Resource usage. /proc/<pid>/usage /proc/<pid>/lwp/<lwpid>/lwpusage */
|
||||
type prusage_t struct {
|
||||
pr_lwpid id_t /* lwp id. 0: process or defunct */
|
||||
pr_count int32 /* number of contributing lwps */
|
||||
// 8
|
||||
pr_tstamp timestruc_t /* current time stamp */
|
||||
pr_create timestruc_t /* process/lwp creation time stamp */
|
||||
pr_term timestruc_t /* process/lwp termination time stamp */
|
||||
pr_rtime timestruc_t /* total lwp real (elapsed) time */
|
||||
// 72
|
||||
pr_utime timestruc_t /* user level cpu time */
|
||||
pr_stime timestruc_t /* system call cpu time */
|
||||
pr_ttime timestruc_t /* other system trap cpu time */
|
||||
pr_tftime timestruc_t /* text page fault sleep time */
|
||||
// 136
|
||||
pr_dftime timestruc_t /* data page fault sleep time */
|
||||
pr_kftime timestruc_t /* kernel page fault sleep time */
|
||||
pr_ltime timestruc_t /* user lock wait sleep time */
|
||||
pr_slptime timestruc_t /* all other sleep time */
|
||||
// 200
|
||||
pr_wtime timestruc_t /* wait-cpu (latency) time */
|
||||
pr_stoptime timestruc_t /* stopped time */
|
||||
// 232
|
||||
filltime [6]timestruc_t /* filler for future expansion */
|
||||
// 328
|
||||
pr_minf ulong_t /* minor page faults */
|
||||
pr_majf ulong_t /* major page faults */
|
||||
pr_nswap ulong_t /* swaps */
|
||||
pr_inblk ulong_t /* input blocks (JEL: disk events not always recorded, so perhaps usable as an indicator but not more) */
|
||||
// 360
|
||||
pr_oublk ulong_t /* output blocks (JEL: disk events not always recorded, so perhaps usable as an indicator but not more) */
|
||||
pr_msnd ulong_t /* messages sent */
|
||||
pr_mrcv ulong_t /* messages received */
|
||||
pr_sigs ulong_t /* signals received */
|
||||
// 392
|
||||
pr_vctx ulong_t /* voluntary context switches */
|
||||
pr_ictx ulong_t /* involuntary context switches */
|
||||
pr_sysc ulong_t /* system calls */
|
||||
pr_ioch ulong_t /* chars read and written (JEL: no matter, whether to/from disk or somewhere else) */
|
||||
// 424
|
||||
filler [10]ulong_t /* filler for future expansion */
|
||||
// 504
|
||||
}
|
||||
|
||||
/** End Of Solaris types **/
|
||||
|
||||
type ProcMetric uint32
|
||||
|
||||
const (
|
||||
PM_OPEN_FDS ProcMetric = iota
|
||||
PM_MAX_FDS
|
||||
PM_MINFLT
|
||||
PM_MAJFLT
|
||||
PM_CPU_UTIL
|
||||
PM_MEM_UTIL
|
||||
PM_CMINFLT // Linux, only
|
||||
PM_CMAJFLT // Linux, only
|
||||
PM_UTIME
|
||||
PM_STIME
|
||||
PM_TIME
|
||||
PM_CUTIME
|
||||
PM_CSTIME
|
||||
PM_CTIME
|
||||
PM_NUM_THREADS
|
||||
PM_STARTTIME
|
||||
PM_VSIZE
|
||||
PM_RSS
|
||||
PM_VCTX
|
||||
PM_ICTX
|
||||
PM_BLKIO // Linux, only
|
||||
PM_COUNT /* contract: must be the last one */
|
||||
)
|
||||
|
||||
type MetricInfo struct {
|
||||
name, help, mtype string
|
||||
}
|
||||
|
||||
/* process metric names and descriptions */
|
||||
var pm_desc = [PM_COUNT]MetricInfo{
|
||||
{ // PM_OPEN_FDS
|
||||
"process_open_fds",
|
||||
"Number of open file descriptors",
|
||||
"gauge",
|
||||
}, { // PM_MAX_FDS
|
||||
"process_max_fds",
|
||||
"Max. number of open file descriptors (soft limit)",
|
||||
"gauge",
|
||||
}, { // PM_MINFLT
|
||||
"process_minor_pagefaults",
|
||||
"Number of minor faults of the process not caused a page load from disk",
|
||||
"counter",
|
||||
}, { // PM_MAJFLT
|
||||
"process_major_pagefaults",
|
||||
"Number of major faults of the process caused a page load from disk",
|
||||
"counter",
|
||||
}, { // PM_CPU_UTIL
|
||||
"process_cpu_utilization_percent",
|
||||
"Percent of recent cpu time used by all lwps",
|
||||
"gauge",
|
||||
}, { // PM_MEM_UTIL
|
||||
"process_mem_utilization_percent",
|
||||
"Percent of system memory used by process",
|
||||
"gauge",
|
||||
}, { // PM_CMINFLT
|
||||
"process_children_minor_pagefaults",
|
||||
"Number of minor faults of the process waited-for children not caused a page load from disk",
|
||||
"counter",
|
||||
}, { // PM_CMAJFLT
|
||||
"process_children_major_pagefaults",
|
||||
"Number of major faults of the process's waited-for children caused a page load from disk",
|
||||
"counter",
|
||||
}, { // PM_UTIME
|
||||
"process_user_cpu_seconds",
|
||||
"Total CPU time the process spent in user mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_STIME
|
||||
"process_system_cpu_seconds",
|
||||
"Total CPU time the process spent in kernel mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_TIME
|
||||
"process_total_cpu_seconds",
|
||||
"Total CPU time the process spent in user and kernel mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_CUTIME
|
||||
"process_children_user_cpu_seconds",
|
||||
"Total CPU time the process's waited-for children spent in user mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_CSTIME
|
||||
"process_children_system_cpu_seconds",
|
||||
"Total CPU time the process's waited-for children spent in kernel mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_CTIME
|
||||
"process_children_total_cpu_seconds",
|
||||
"Total CPU time the process's waited-for children spent in user and in kernel mode in seconds",
|
||||
"counter",
|
||||
}, { // PM_NUM_THREADS
|
||||
"process_threads_total",
|
||||
"Number of threads in this process",
|
||||
"gauge",
|
||||
}, { // PM_STARTTIME
|
||||
"process_start_time_seconds",
|
||||
"The time the process has been started in seconds elapsed since Epoch",
|
||||
"counter",
|
||||
}, { // PM_VSIZE
|
||||
"process_virtual_memory_bytes",
|
||||
"Virtual memory size in bytes",
|
||||
"gauge",
|
||||
}, { // PM_RSS
|
||||
"process_resident_memory_bytes",
|
||||
"Resident set size of memory in bytes",
|
||||
"gauge",
|
||||
}, { // PM_VCTX
|
||||
"process_voluntary_ctxsw_total",
|
||||
"Number of voluntary context switches",
|
||||
"counter",
|
||||
}, { // PM_ICTX
|
||||
"process_involuntary_ctxsw_total",
|
||||
"Number of involuntary context switches",
|
||||
"counter",
|
||||
}, { // PM_BLKIO
|
||||
"process_delayacct_blkio_ticks",
|
||||
"Aggregated block I/O delays, measured in clock ticks (centiseconds)",
|
||||
"counter",
|
||||
},
|
||||
}
|
||||
|
||||
type ProcFd uint32
|
||||
|
||||
const (
|
||||
FD_LIMITS ProcFd = iota
|
||||
FD_STAT
|
||||
FD_PSINFO // solaris/illumos, only
|
||||
FD_USAGE // solaris/illumos, only
|
||||
FD_COUNT /* contract: must be the last one */
|
||||
)
|
||||
|
||||
/* emittable process metrics for solaris */
|
||||
var activeProcMetrics = []ProcMetric{
|
||||
PM_MINFLT,
|
||||
PM_MAJFLT,
|
||||
PM_CPU_UTIL,
|
||||
PM_MEM_UTIL,
|
||||
PM_UTIME,
|
||||
PM_STIME,
|
||||
PM_TIME,
|
||||
PM_CUTIME,
|
||||
PM_CSTIME,
|
||||
PM_CTIME,
|
||||
PM_NUM_THREADS,
|
||||
PM_STARTTIME,
|
||||
PM_VSIZE,
|
||||
PM_RSS,
|
||||
PM_VCTX,
|
||||
PM_ICTX,
|
||||
}
|
||||
|
||||
/* emittable fd metrics for solaris */
|
||||
var activeFdMetrics = []ProcMetric{
|
||||
PM_OPEN_FDS,
|
||||
PM_MAX_FDS,
|
||||
}
|
||||
|
||||
/*
|
||||
process metrics related file descriptors for files we always need, and
|
||||
|
||||
do not want to open/close all the time
|
||||
*/
|
||||
var pm_fd [FD_COUNT]int
|
||||
|
||||
/*
|
||||
to avaid, that go closes the files in the background, which makes the FDs
|
||||
|
||||
above useless, we need to keep the reference to them as well
|
||||
*/
|
||||
var pm_file [FD_COUNT]*os.File
|
||||
|
||||
/*
|
||||
process metric values. TSDBs use internally always float64, so we do not
|
||||
|
||||
need to make a difference between int and non-int values
|
||||
*/
|
||||
var pm_val [PM_COUNT]float64
|
||||
|
||||
/* path used to count open FDs */
|
||||
var fd_path string
|
||||
|
||||
/* lazy init of this process related metrics */
|
||||
func init() {
|
||||
var testdata_dir = ""
|
||||
var onTest = len(os.Args) > 1 && strings.HasSuffix(os.Args[0], ".test")
|
||||
if onTest {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic("Unknwon directory: " + err.Error())
|
||||
}
|
||||
testdata_dir = cwd + "/testdata"
|
||||
fmt.Printf("Using test data in %s ...\n", testdata_dir)
|
||||
}
|
||||
|
||||
// we preset all so that it is safe to use these vals even if the rest of
|
||||
// init fails
|
||||
for i := 0; i < int(PM_COUNT); i++ {
|
||||
pm_val[i] = 0
|
||||
}
|
||||
for i := 0; i < int(FD_COUNT); i++ {
|
||||
pm_fd[i] = -1
|
||||
}
|
||||
pid := os.Getpid()
|
||||
if onTest {
|
||||
fd_path = testdata_dir + "/fd"
|
||||
} else {
|
||||
fd_path = fmt.Sprintf("/proc/%d/fd", pid)
|
||||
}
|
||||
|
||||
// NOTE: We do NOT close these FDs intentionally to avoid the open/close
|
||||
// overhead for each update.
|
||||
var path string
|
||||
if onTest {
|
||||
path = fmt.Sprintf(testdata_dir + "/solaris.ps_status")
|
||||
} else {
|
||||
path = fmt.Sprintf("/proc/%d/status", pid)
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: Unable to open %s (%v).", path, err)
|
||||
} else {
|
||||
pm_file[FD_STAT] = f
|
||||
pm_fd[FD_STAT] = int(f.Fd())
|
||||
}
|
||||
if onTest {
|
||||
path = fmt.Sprintf(testdata_dir + "/solaris.ps_info")
|
||||
} else {
|
||||
path = fmt.Sprintf("/proc/%d/psinfo", pid)
|
||||
}
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: Unable to open %s (%v).", path, err)
|
||||
} else {
|
||||
pm_file[FD_PSINFO] = f
|
||||
pm_fd[FD_PSINFO] = int(f.Fd())
|
||||
}
|
||||
if onTest {
|
||||
path = fmt.Sprintf(testdata_dir + "/solaris.ps_usage")
|
||||
} else {
|
||||
path = fmt.Sprintf("/proc/%d/usage", pid)
|
||||
}
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: Unable to open %s (%v).", path, err)
|
||||
} else {
|
||||
pm_file[FD_USAGE] = f
|
||||
pm_fd[FD_USAGE] = int(f.Fd())
|
||||
}
|
||||
|
||||
/* usually an app does|cannot not change its own FD limits. So we handle
|
||||
it as a const - determine it once, only */
|
||||
var lim syscall.Rlimit
|
||||
err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
||||
if err == nil {
|
||||
pm_val[PM_MAX_FDS] = float64(lim.Cur)
|
||||
} else {
|
||||
log.Printf("ERROR: metrics: Unable determin max. fd limit.")
|
||||
pm_val[PM_MAX_FDS] = -1
|
||||
}
|
||||
}
|
||||
|
||||
var nan = math.NaN()
|
||||
|
||||
func time2float(t timestruc_t) float64 {
|
||||
return float64(t.tv_sec) + float64(t.tv_nsec)*1e-9
|
||||
}
|
||||
func time2float2(a timestruc_t, b timestruc_t) float64 {
|
||||
return float64(a.tv_sec+b.tv_sec) + float64(a.tv_nsec+b.tv_nsec)*1e-9
|
||||
}
|
||||
|
||||
func updateProcMetrics() {
|
||||
var status pstatus_t
|
||||
var psinfo psinfo_t
|
||||
var usage prusage_t
|
||||
|
||||
var fail = pm_fd[FD_STAT] < 0
|
||||
if !fail {
|
||||
n, err := syscall.Pread(pm_fd[FD_STAT],
|
||||
(*(*[unsafe.Sizeof(status)]byte)(unsafe.Pointer(&status)))[:], 0)
|
||||
fail = (n < 324 || err != nil)
|
||||
if fail {
|
||||
fmt.Printf("WARNING: read %s@%d failed: %v\n",
|
||||
pm_file[FD_STAT].Name(), n, err)
|
||||
}
|
||||
}
|
||||
if fail {
|
||||
pm_val[PM_NUM_THREADS] = nan
|
||||
pm_val[PM_UTIME] = nan
|
||||
pm_val[PM_STIME] = nan
|
||||
pm_val[PM_TIME] = nan
|
||||
pm_val[PM_CUTIME] = nan
|
||||
pm_val[PM_CSTIME] = nan
|
||||
pm_val[PM_CTIME] = nan
|
||||
} else {
|
||||
pm_val[PM_NUM_THREADS] = float64(status.pr_nlwp + status.pr_nzomb)
|
||||
pm_val[PM_UTIME] = time2float(status.pr_utime)
|
||||
pm_val[PM_STIME] = time2float(status.pr_stime)
|
||||
pm_val[PM_TIME] = time2float2(status.pr_utime, status.pr_stime)
|
||||
pm_val[PM_CUTIME] = time2float(status.pr_cutime)
|
||||
pm_val[PM_CSTIME] = time2float(status.pr_cstime)
|
||||
pm_val[PM_CTIME] = time2float2(status.pr_cutime, status.pr_cstime)
|
||||
}
|
||||
fail = pm_fd[FD_PSINFO] < 0
|
||||
if !fail {
|
||||
n, err := syscall.Pread(pm_fd[FD_PSINFO],
|
||||
(*(*[unsafe.Sizeof(psinfo)]byte)(unsafe.Pointer(&psinfo)))[:], 0)
|
||||
fail = (n < 272 || err != nil)
|
||||
if fail {
|
||||
fmt.Printf("WARNING: read %s@%d failed: %v\n",
|
||||
pm_file[FD_PSINFO].Name(), n, err)
|
||||
}
|
||||
}
|
||||
if fail {
|
||||
pm_val[PM_VSIZE] = nan
|
||||
pm_val[PM_RSS] = nan
|
||||
pm_val[PM_CPU_UTIL] = nan
|
||||
pm_val[PM_MEM_UTIL] = nan
|
||||
pm_val[PM_STARTTIME] = nan
|
||||
} else {
|
||||
//num_threads = psinfo.pr_nlwp + psinfo.pr_nzomb // already by status
|
||||
pm_val[PM_VSIZE] = float64(psinfo.pr_size << 10)
|
||||
pm_val[PM_RSS] = float64(psinfo.pr_rssize << 10)
|
||||
pm_val[PM_CPU_UTIL] = 100 * float64(psinfo.pr_pctcpu) / float64(0x8000)
|
||||
pm_val[PM_MEM_UTIL] = 100 * float64(psinfo.pr_pctmem) / float64(0x8000)
|
||||
pm_val[PM_STARTTIME] = float64(psinfo.pr_start.tv_sec)
|
||||
}
|
||||
fail = pm_fd[FD_USAGE] < 0
|
||||
if !fail {
|
||||
n, err := syscall.Pread(pm_fd[FD_USAGE],
|
||||
(*(*[unsafe.Sizeof(usage)]byte)(unsafe.Pointer(&usage)))[:], 0)
|
||||
fail = (n < 424 || err != nil)
|
||||
if fail {
|
||||
fmt.Printf("WARNING: read %s@%d failed: %v\n",
|
||||
pm_file[FD_USAGE].Name(), n, err)
|
||||
}
|
||||
}
|
||||
if fail {
|
||||
pm_val[PM_MINFLT] = nan
|
||||
pm_val[PM_MAJFLT] = nan
|
||||
pm_val[PM_VCTX] = nan
|
||||
pm_val[PM_ICTX] = nan
|
||||
} else {
|
||||
pm_val[PM_MINFLT] = float64(usage.pr_minf)
|
||||
pm_val[PM_MAJFLT] = float64(usage.pr_majf)
|
||||
pm_val[PM_VCTX] = float64(usage.pr_vctx)
|
||||
pm_val[PM_ICTX] = float64(usage.pr_ictx)
|
||||
}
|
||||
}
|
||||
|
||||
func updateFdMetrics() {
|
||||
pm_val[PM_OPEN_FDS] = 0
|
||||
f, err := os.Open(fd_path)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: Unable to open %s", fd_path)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
for {
|
||||
names, err := f.Readdirnames(512)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: Read error for %s: %s", fd_path, err)
|
||||
return
|
||||
}
|
||||
pm_val[PM_OPEN_FDS] += float64(len(names))
|
||||
}
|
||||
}
|
||||
|
||||
func writeProcessMetrics(w io.Writer) {
|
||||
updateProcMetrics()
|
||||
if isMetadataEnabled() {
|
||||
for _, v := range activeProcMetrics {
|
||||
fmt.Fprintf(w, "# HELP %s %s\n# TYPE %s %s\n%s %.17g\n",
|
||||
pm_desc[v].name, pm_desc[v].help,
|
||||
pm_desc[v].name, pm_desc[v].mtype,
|
||||
pm_desc[v].name, pm_val[v])
|
||||
}
|
||||
} else {
|
||||
for _, v := range activeProcMetrics {
|
||||
fmt.Fprintf(w, "%s %.17g\n", pm_desc[v].name, pm_val[v])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeFDMetrics(w io.Writer) {
|
||||
updateFdMetrics()
|
||||
if isMetadataEnabled() {
|
||||
for _, v := range activeFdMetrics {
|
||||
fmt.Fprintf(w, "# HELP %s %s\n# TYPE %s %s\n%s %.17g\n",
|
||||
pm_desc[v].name, pm_desc[v].help,
|
||||
pm_desc[v].name, pm_desc[v].mtype,
|
||||
pm_desc[v].name, pm_val[v])
|
||||
}
|
||||
} else {
|
||||
for _, v := range activeFdMetrics {
|
||||
fmt.Fprintf(w, "%s %.17g\n", pm_desc[v].name, pm_val[v])
|
||||
}
|
||||
}
|
||||
}
|
||||
83
vendor/github.com/VictoriaMetrics/metrics/process_metrics_windows.go
generated
vendored
Normal file
83
vendor/github.com/VictoriaMetrics/metrics/process_metrics_windows.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
//go:build windows
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modpsapi = syscall.NewLazyDLL("psapi.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessmemoryinfo
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo")
|
||||
procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount")
|
||||
)
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters_ex
|
||||
type processMemoryCounters struct {
|
||||
_ uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uintptr
|
||||
WorkingSetSize uintptr
|
||||
QuotaPeakPagedPoolUsage uintptr
|
||||
QuotaPagedPoolUsage uintptr
|
||||
QuotaPeakNonPagedPoolUsage uintptr
|
||||
QuotaNonPagedPoolUsage uintptr
|
||||
PagefileUsage uintptr
|
||||
PeakPagefileUsage uintptr
|
||||
PrivateUsage uintptr
|
||||
}
|
||||
|
||||
func writeProcessMetrics(w io.Writer) {
|
||||
h := windows.CurrentProcess()
|
||||
var startTime, exitTime, stime, utime windows.Filetime
|
||||
err := windows.GetProcessTimes(h, &startTime, &exitTime, &stime, &utime)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics: cannot read process times: %s", err)
|
||||
return
|
||||
}
|
||||
var mc processMemoryCounters
|
||||
r1, _, err := procGetProcessMemoryInfo.Call(
|
||||
uintptr(h),
|
||||
uintptr(unsafe.Pointer(&mc)),
|
||||
unsafe.Sizeof(mc),
|
||||
)
|
||||
if r1 != 1 {
|
||||
log.Printf("ERROR: metrics: cannot read process memory information: %s", err)
|
||||
return
|
||||
}
|
||||
stimeSeconds := float64(uint64(stime.HighDateTime)<<32+uint64(stime.LowDateTime)) / 1e7
|
||||
utimeSeconds := float64(uint64(utime.HighDateTime)<<32+uint64(utime.LowDateTime)) / 1e7
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_system_total", stimeSeconds)
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_total", stimeSeconds+utimeSeconds)
|
||||
WriteCounterFloat64(w, "process_cpu_seconds_user_total", stimeSeconds)
|
||||
WriteCounterUint64(w, "process_pagefaults_total", uint64(mc.PageFaultCount))
|
||||
WriteGaugeUint64(w, "process_start_time_seconds", uint64(startTime.Nanoseconds())/1e9)
|
||||
WriteGaugeUint64(w, "process_virtual_memory_bytes", uint64(mc.PrivateUsage))
|
||||
WriteGaugeUint64(w, "process_resident_memory_peak_bytes", uint64(mc.PeakWorkingSetSize))
|
||||
WriteGaugeUint64(w, "process_resident_memory_bytes", uint64(mc.WorkingSetSize))
|
||||
}
|
||||
|
||||
func writeFDMetrics(w io.Writer) {
|
||||
h := windows.CurrentProcess()
|
||||
var count uint32
|
||||
r1, _, err := procGetProcessHandleCount.Call(
|
||||
uintptr(h),
|
||||
uintptr(unsafe.Pointer(&count)),
|
||||
)
|
||||
if r1 != 1 {
|
||||
log.Printf("ERROR: metrics: cannot determine open file descriptors count: %s", err)
|
||||
return
|
||||
}
|
||||
// it seems to be hard-coded limit for 64-bit systems
|
||||
// https://learn.microsoft.com/en-us/archive/blogs/markrussinovich/pushing-the-limits-of-windows-handles#maximum-number-of-handles
|
||||
WriteGaugeUint64(w, "process_max_fds", 16777216)
|
||||
WriteGaugeUint64(w, "process_open_fds", uint64(count))
|
||||
}
|
||||
273
vendor/github.com/VictoriaMetrics/metrics/prometheus_histogram.go
generated
vendored
Normal file
273
vendor/github.com/VictoriaMetrics/metrics/prometheus_histogram.go
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PrometheusHistogramDefaultBuckets is a list of the default bucket upper
|
||||
// bounds. Those default buckets are quite generic, and it is recommended to
|
||||
// pick custom buckets for improved accuracy.
|
||||
var PrometheusHistogramDefaultBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
|
||||
|
||||
// PrometheusHistogram is a histogram for non-negative values with pre-defined buckets
|
||||
//
|
||||
// Each bucket contains a counter for values in the given range.
|
||||
// Each bucket is exposed via the following metric:
|
||||
//
|
||||
// <metric_name>_bucket{<optional_tags>,le="upper_bound"} <counter>
|
||||
//
|
||||
// Where:
|
||||
//
|
||||
// - <metric_name> is the metric name passed to NewPrometheusHistogram
|
||||
// - <optional_tags> is optional tags for the <metric_name>, which are passed to NewPrometheusHistogram
|
||||
// - <upper_bound> - upper bound of the current bucket. all samples <= upper_bound are in that bucket
|
||||
// - <counter> - the number of hits to the given bucket during Update* calls
|
||||
//
|
||||
// Next to the bucket metrics, two additional metrics track the total number of
|
||||
// samples (_count) and the total sum (_sum) of all samples:
|
||||
//
|
||||
// - <metric_name>_sum{<optional_tags>} <counter>
|
||||
// - <metric_name>_count{<optional_tags>} <counter>
|
||||
type PrometheusHistogram struct {
|
||||
// mu guarantees synchronous update for all the counters.
|
||||
//
|
||||
// Do not use sync.RWMutex, since it has zero sense from performance PoV.
|
||||
// It only complicates the code.
|
||||
mu sync.Mutex
|
||||
|
||||
// upperBounds and buckets are aligned by element position:
|
||||
// upperBounds[i] defines the upper bound for buckets[i].
|
||||
// buckets[i] contains the count of elements <= upperBounds[i]
|
||||
upperBounds []float64
|
||||
buckets []uint64
|
||||
|
||||
// count is the counter for all observations on this histogram
|
||||
count uint64
|
||||
|
||||
// sum is the sum of all the values put into Histogram
|
||||
sum float64
|
||||
}
|
||||
|
||||
// Reset resets previous observations in h.
|
||||
func (h *PrometheusHistogram) Reset() {
|
||||
h.mu.Lock()
|
||||
for i := range h.buckets {
|
||||
h.buckets[i] = 0
|
||||
}
|
||||
h.sum = 0
|
||||
h.count = 0
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// Update updates h with v.
|
||||
//
|
||||
// Negative values and NaNs are ignored.
|
||||
func (h *PrometheusHistogram) Update(v float64) {
|
||||
if math.IsNaN(v) || v < 0 {
|
||||
// Skip NaNs and negative values.
|
||||
return
|
||||
}
|
||||
bucketIdx := -1
|
||||
for i, ub := range h.upperBounds {
|
||||
if v <= ub {
|
||||
bucketIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
h.mu.Lock()
|
||||
h.sum += v
|
||||
h.count++
|
||||
if bucketIdx == -1 {
|
||||
// +Inf, nothing to do, already accounted for in the total sum
|
||||
h.mu.Unlock()
|
||||
return
|
||||
}
|
||||
h.buckets[bucketIdx]++
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// UpdateDuration updates request duration based on the given startTime.
|
||||
func (h *PrometheusHistogram) UpdateDuration(startTime time.Time) {
|
||||
d := time.Since(startTime).Seconds()
|
||||
h.Update(d)
|
||||
}
|
||||
|
||||
// NewPrometheusHistogram creates and returns new PrometheusHistogram with the given name
|
||||
// and PrometheusHistogramDefaultBuckets.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func NewPrometheusHistogram(name string) *PrometheusHistogram {
|
||||
return defaultSet.NewPrometheusHistogram(name)
|
||||
}
|
||||
|
||||
// NewPrometheusHistogramExt creates and returns new PrometheusHistogram with the given name
|
||||
// and given upperBounds.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func NewPrometheusHistogramExt(name string, upperBounds []float64) *PrometheusHistogram {
|
||||
return defaultSet.NewPrometheusHistogramExt(name, upperBounds)
|
||||
}
|
||||
|
||||
// GetOrCreatePrometheusHistogram returns registered PrometheusHistogram with the given name
|
||||
// or creates a new PrometheusHistogram if the registry doesn't contain histogram with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewPrometheusHistogram instead of GetOrCreatePrometheusHistogram.
|
||||
func GetOrCreatePrometheusHistogram(name string) *PrometheusHistogram {
|
||||
return defaultSet.GetOrCreatePrometheusHistogram(name)
|
||||
}
|
||||
|
||||
// GetOrCreatePrometheusHistogramExt returns registered PrometheusHistogram with the given name and
|
||||
// upperBounds or creates new PrometheusHistogram if the registry doesn't contain histogram
|
||||
// with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewPrometheusHistogramExt instead of GetOrCreatePrometheusHistogramExt.
|
||||
func GetOrCreatePrometheusHistogramExt(name string, upperBounds []float64) *PrometheusHistogram {
|
||||
return defaultSet.GetOrCreatePrometheusHistogramExt(name, upperBounds)
|
||||
}
|
||||
|
||||
func newPrometheusHistogram(upperBounds []float64) *PrometheusHistogram {
|
||||
mustValidateBuckets(upperBounds)
|
||||
last := len(upperBounds) - 1
|
||||
if math.IsInf(upperBounds[last], +1) {
|
||||
upperBounds = upperBounds[:last] // ignore +Inf bucket as it is covered anyways
|
||||
}
|
||||
h := PrometheusHistogram{
|
||||
upperBounds: upperBounds,
|
||||
buckets: make([]uint64, len(upperBounds)),
|
||||
}
|
||||
|
||||
return &h
|
||||
}
|
||||
|
||||
func mustValidateBuckets(upperBounds []float64) {
|
||||
if err := ValidateBuckets(upperBounds); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateBuckets validates the given upperBounds and returns an error
|
||||
// if validation failed.
|
||||
func ValidateBuckets(upperBounds []float64) error {
|
||||
if len(upperBounds) == 0 {
|
||||
return fmt.Errorf("upperBounds can't be empty")
|
||||
}
|
||||
for i := 0; i < len(upperBounds)-1; i++ {
|
||||
if upperBounds[i] >= upperBounds[i+1] {
|
||||
return fmt.Errorf("upper bounds for the buckets must be strictly increasing")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LinearBuckets returns a list of upperBounds for PrometheusHistogram,
|
||||
// and whose distribution is as follows:
|
||||
//
|
||||
// [start, start + width, start + 2 * width, ... start + (count-1) * width]
|
||||
//
|
||||
// Panics if given start, width and count produce negative buckets or none buckets at all.
|
||||
func LinearBuckets(start, width float64, count int) []float64 {
|
||||
if count < 1 {
|
||||
panic("LinearBuckets: count can't be less than 1")
|
||||
}
|
||||
upperBounds := make([]float64, count)
|
||||
for i := range upperBounds {
|
||||
upperBounds[i] = start
|
||||
start += width
|
||||
}
|
||||
mustValidateBuckets(upperBounds)
|
||||
return upperBounds
|
||||
}
|
||||
|
||||
// ExponentialBuckets returns a list of upperBounds for PrometheusHistogram,
|
||||
// and whose distribution is as follows:
|
||||
//
|
||||
// [start, start * factor pow 1, start * factor pow 2, ... start * factor pow (count-1)]
|
||||
//
|
||||
// Panics if given start, width and count produce negative buckets or none buckets at all.
|
||||
func ExponentialBuckets(start, factor float64, count int) []float64 {
|
||||
if count < 1 {
|
||||
panic("ExponentialBuckets: count can't be less than 1")
|
||||
}
|
||||
if factor <= 1 {
|
||||
panic("ExponentialBuckets: factor must be greater than 1")
|
||||
}
|
||||
if start <= 0 {
|
||||
panic("ExponentialBuckets: start can't be less than 0")
|
||||
}
|
||||
upperBounds := make([]float64, count)
|
||||
for i := range upperBounds {
|
||||
upperBounds[i] = start
|
||||
start *= factor
|
||||
}
|
||||
mustValidateBuckets(upperBounds)
|
||||
return upperBounds
|
||||
}
|
||||
|
||||
func (h *PrometheusHistogram) marshalTo(prefix string, w io.Writer) {
|
||||
cumulativeSum := uint64(0)
|
||||
h.mu.Lock()
|
||||
count := h.count
|
||||
sum := h.sum
|
||||
for i, ub := range h.upperBounds {
|
||||
cumulativeSum += h.buckets[i]
|
||||
tag := fmt.Sprintf(`le="%v"`, ub)
|
||||
metricName := addTag(prefix, tag)
|
||||
name, labels := splitMetricName(metricName)
|
||||
fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, cumulativeSum)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
|
||||
tag := fmt.Sprintf("le=%q", "+Inf")
|
||||
metricName := addTag(prefix, tag)
|
||||
name, labels := splitMetricName(metricName)
|
||||
fmt.Fprintf(w, "%s_bucket%s %d\n", name, labels, count)
|
||||
|
||||
name, labels = splitMetricName(prefix)
|
||||
if float64(int64(sum)) == sum {
|
||||
fmt.Fprintf(w, "%s_sum%s %d\n", name, labels, int64(sum))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s_sum%s %g\n", name, labels, sum)
|
||||
}
|
||||
fmt.Fprintf(w, "%s_count%s %d\n", name, labels, count)
|
||||
}
|
||||
|
||||
func (h *PrometheusHistogram) metricType() string {
|
||||
return "histogram"
|
||||
}
|
||||
510
vendor/github.com/VictoriaMetrics/metrics/push.go
generated
vendored
Normal file
510
vendor/github.com/VictoriaMetrics/metrics/push.go
generated
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"compress/gzip"
|
||||
)
|
||||
|
||||
// PushOptions is the list of options, which may be applied to InitPushWithOptions().
|
||||
type PushOptions struct {
|
||||
// ExtraLabels is an optional comma-separated list of `label="value"` labels, which must be added to all the metrics before pushing them to pushURL.
|
||||
ExtraLabels string
|
||||
|
||||
// Headers is an optional list of HTTP headers to add to every push request to pushURL.
|
||||
//
|
||||
// Every item in the list must have the form `Header: value`. For example, `Authorization: Custom my-top-secret`.
|
||||
Headers []string
|
||||
|
||||
// Whether to disable HTTP request body compression before sending the metrics to pushURL.
|
||||
//
|
||||
// By default the compression is enabled.
|
||||
DisableCompression bool
|
||||
|
||||
// Method is HTTP request method to use when pushing metrics to pushURL.
|
||||
//
|
||||
// By default the Method is GET.
|
||||
Method string
|
||||
|
||||
// Optional WaitGroup for waiting until all the push workers created with this WaitGroup are stopped.
|
||||
WaitGroup *sync.WaitGroup
|
||||
}
|
||||
|
||||
// InitPushWithOptions sets up periodic push for globally registered metrics to the given pushURL with the given interval.
|
||||
//
|
||||
// The periodic push is stopped when ctx is canceled.
|
||||
// It is possible to wait until the background metrics push worker is stopped on a WaitGroup passed via opts.WaitGroup.
|
||||
//
|
||||
// If pushProcessMetrics is set to true, then 'process_*' and `go_*` metrics are also pushed to pushURL.
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// The metrics are pushed to pushURL in Prometheus text exposition format.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPushWithOptions multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
func InitPushWithOptions(ctx context.Context, pushURL string, interval time.Duration, pushProcessMetrics bool, opts *PushOptions) error {
|
||||
writeMetrics := func(w io.Writer) {
|
||||
WritePrometheus(w, pushProcessMetrics)
|
||||
}
|
||||
return InitPushExtWithOptions(ctx, pushURL, interval, writeMetrics, opts)
|
||||
}
|
||||
|
||||
// InitPushProcessMetrics sets up periodic push for 'process_*' metrics to the given pushURL with the given interval.
|
||||
//
|
||||
// extraLabels may contain comma-separated list of `label="value"` labels, which will be added
|
||||
// to all the metrics before pushing them to pushURL.
|
||||
//
|
||||
// The metrics are pushed to pushURL in Prometheus text exposition format.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPushProcessMetrics multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
func InitPushProcessMetrics(pushURL string, interval time.Duration, extraLabels string) error {
|
||||
return InitPushExt(pushURL, interval, extraLabels, WriteProcessMetrics)
|
||||
}
|
||||
|
||||
// InitPush sets up periodic push for globally registered metrics to the given pushURL with the given interval.
|
||||
//
|
||||
// extraLabels may contain comma-separated list of `label="value"` labels, which will be added
|
||||
// to all the metrics before pushing them to pushURL.
|
||||
//
|
||||
// If pushProcessMetrics is set to true, then 'process_*' and `go_*` metrics are also pushed to pushURL.
|
||||
//
|
||||
// The metrics are pushed to pushURL in Prometheus text exposition format.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPush multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
func InitPush(pushURL string, interval time.Duration, extraLabels string, pushProcessMetrics bool) error {
|
||||
writeMetrics := func(w io.Writer) {
|
||||
WritePrometheus(w, pushProcessMetrics)
|
||||
}
|
||||
return InitPushExt(pushURL, interval, extraLabels, writeMetrics)
|
||||
}
|
||||
|
||||
// PushMetrics pushes globally registered metrics to pushURL.
|
||||
//
|
||||
// If pushProcessMetrics is set to true, then 'process_*' and `go_*` metrics are also pushed to pushURL.
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
func PushMetrics(ctx context.Context, pushURL string, pushProcessMetrics bool, opts *PushOptions) error {
|
||||
writeMetrics := func(w io.Writer) {
|
||||
WritePrometheus(w, pushProcessMetrics)
|
||||
}
|
||||
return PushMetricsExt(ctx, pushURL, writeMetrics, opts)
|
||||
}
|
||||
|
||||
// InitPushWithOptions sets up periodic push for metrics from s to the given pushURL with the given interval.
|
||||
//
|
||||
// The periodic push is stopped when the ctx is canceled.
|
||||
// It is possible to wait until the background metrics push worker is stopped on a WaitGroup passed via opts.WaitGroup.
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// The metrics are pushed to pushURL in Prometheus text exposition format.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPushWithOptions multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
func (s *Set) InitPushWithOptions(ctx context.Context, pushURL string, interval time.Duration, opts *PushOptions) error {
|
||||
return InitPushExtWithOptions(ctx, pushURL, interval, s.WritePrometheus, opts)
|
||||
}
|
||||
|
||||
// InitPush sets up periodic push for metrics from s to the given pushURL with the given interval.
|
||||
//
|
||||
// extraLabels may contain comma-separated list of `label="value"` labels, which will be added
|
||||
// to all the metrics before pushing them to pushURL.
|
||||
//
|
||||
// The metrics are pushed to pushURL in Prometheus text exposition format.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPush multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
func (s *Set) InitPush(pushURL string, interval time.Duration, extraLabels string) error {
|
||||
return InitPushExt(pushURL, interval, extraLabels, s.WritePrometheus)
|
||||
}
|
||||
|
||||
// PushMetrics pushes s metrics to pushURL.
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
func (s *Set) PushMetrics(ctx context.Context, pushURL string, opts *PushOptions) error {
|
||||
return PushMetricsExt(ctx, pushURL, s.WritePrometheus, opts)
|
||||
}
|
||||
|
||||
// InitPushExt sets up periodic push for metrics obtained by calling writeMetrics with the given interval.
|
||||
//
|
||||
// extraLabels may contain comma-separated list of `label="value"` labels, which will be added
|
||||
// to all the metrics before pushing them to pushURL.
|
||||
//
|
||||
// The writeMetrics callback must write metrics to w in Prometheus text exposition format without timestamps and trailing comments.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPushExt multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
//
|
||||
// It is OK calling InitPushExt multiple times with different writeMetrics -
|
||||
// in this case all the metrics generated by writeMetrics callbacks are written to pushURL.
|
||||
func InitPushExt(pushURL string, interval time.Duration, extraLabels string, writeMetrics func(w io.Writer)) error {
|
||||
opts := &PushOptions{
|
||||
ExtraLabels: extraLabels,
|
||||
}
|
||||
return InitPushExtWithOptions(context.Background(), pushURL, interval, writeMetrics, opts)
|
||||
}
|
||||
|
||||
// InitPushExtWithOptions sets up periodic push for metrics obtained by calling writeMetrics with the given interval.
|
||||
//
|
||||
// The writeMetrics callback must write metrics to w in Prometheus text exposition format without timestamps and trailing comments.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// The periodic push is stopped when the ctx is canceled.
|
||||
// It is possible to wait until the background metrics push worker is stopped on a WaitGroup passed via opts.WaitGroup.
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
//
|
||||
// It is OK calling InitPushExtWithOptions multiple times with different pushURL -
|
||||
// in this case metrics are pushed to all the provided pushURL urls.
|
||||
//
|
||||
// It is OK calling InitPushExtWithOptions multiple times with different writeMetrics -
|
||||
// in this case all the metrics generated by writeMetrics callbacks are written to pushURL.
|
||||
func InitPushExtWithOptions(ctx context.Context, pushURL string, interval time.Duration, writeMetrics func(w io.Writer), opts *PushOptions) error {
|
||||
pc, err := newPushContext(pushURL, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate interval
|
||||
if interval <= 0 {
|
||||
return fmt.Errorf("interval must be positive; got %s", interval)
|
||||
}
|
||||
pushMetricsSet.GetOrCreateFloatCounter(fmt.Sprintf(`metrics_push_interval_seconds{url=%q}`, pc.pushURLRedacted)).Set(interval.Seconds())
|
||||
|
||||
var wg *sync.WaitGroup
|
||||
if opts != nil {
|
||||
wg = opts.WaitGroup
|
||||
if wg != nil {
|
||||
wg.Add(1)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
stopCh := ctx.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ctxLocal, cancel := context.WithTimeout(ctx, interval+time.Second)
|
||||
err := pc.pushMetrics(ctxLocal, writeMetrics)
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Printf("ERROR: metrics.push: %s", err)
|
||||
}
|
||||
case <-stopCh:
|
||||
if wg != nil {
|
||||
wg.Done()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushMetricsExt pushes metrics generated by wirteMetrics to pushURL.
|
||||
//
|
||||
// The writeMetrics callback must write metrics to w in Prometheus text exposition format without timestamps and trailing comments.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// opts may contain additional configuration options if non-nil.
|
||||
//
|
||||
// It is recommended pushing metrics to /api/v1/import/prometheus endpoint according to
|
||||
// https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/#how-to-import-data-in-prometheus-exposition-format
|
||||
func PushMetricsExt(ctx context.Context, pushURL string, writeMetrics func(w io.Writer), opts *PushOptions) error {
|
||||
pc, err := newPushContext(pushURL, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pc.pushMetrics(ctx, writeMetrics)
|
||||
}
|
||||
|
||||
type pushContext struct {
|
||||
pushURL *url.URL
|
||||
method string
|
||||
pushURLRedacted string
|
||||
extraLabels string
|
||||
headers http.Header
|
||||
disableCompression bool
|
||||
|
||||
client *http.Client
|
||||
|
||||
pushesTotal *Counter
|
||||
bytesPushedTotal *Counter
|
||||
pushBlockSize *Histogram
|
||||
pushDuration *Histogram
|
||||
pushErrors *Counter
|
||||
}
|
||||
|
||||
func newPushContext(pushURL string, opts *PushOptions) (*pushContext, error) {
|
||||
if opts == nil {
|
||||
opts = &PushOptions{}
|
||||
}
|
||||
|
||||
// validate pushURL
|
||||
pu, err := url.Parse(pushURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse pushURL=%q: %w", pushURL, err)
|
||||
}
|
||||
if pu.Scheme != "http" && pu.Scheme != "https" {
|
||||
return nil, fmt.Errorf("unsupported scheme in pushURL=%q; expecting 'http' or 'https'", pushURL)
|
||||
}
|
||||
if pu.Host == "" {
|
||||
return nil, fmt.Errorf("missing host in pushURL=%q", pushURL)
|
||||
}
|
||||
|
||||
method := opts.Method
|
||||
if method == "" {
|
||||
method = http.MethodGet
|
||||
}
|
||||
|
||||
// validate ExtraLabels
|
||||
extraLabels := opts.ExtraLabels
|
||||
if err := validateTags(extraLabels); err != nil {
|
||||
return nil, fmt.Errorf("invalid extraLabels=%q: %w", extraLabels, err)
|
||||
}
|
||||
|
||||
// validate Headers
|
||||
headers := make(http.Header)
|
||||
for _, h := range opts.Headers {
|
||||
n := strings.IndexByte(h, ':')
|
||||
if n < 0 {
|
||||
return nil, fmt.Errorf("missing `:` delimiter in the header %q", h)
|
||||
}
|
||||
name := strings.TrimSpace(h[:n])
|
||||
value := strings.TrimSpace(h[n+1:])
|
||||
headers.Add(name, value)
|
||||
}
|
||||
|
||||
pushURLRedacted := pu.Redacted()
|
||||
client := &http.Client{}
|
||||
return &pushContext{
|
||||
pushURL: pu,
|
||||
method: method,
|
||||
pushURLRedacted: pushURLRedacted,
|
||||
extraLabels: extraLabels,
|
||||
headers: headers,
|
||||
disableCompression: opts.DisableCompression,
|
||||
|
||||
client: client,
|
||||
|
||||
pushesTotal: pushMetricsSet.GetOrCreateCounter(fmt.Sprintf(`metrics_push_total{url=%q}`, pushURLRedacted)),
|
||||
bytesPushedTotal: pushMetricsSet.GetOrCreateCounter(fmt.Sprintf(`metrics_push_bytes_pushed_total{url=%q}`, pushURLRedacted)),
|
||||
pushBlockSize: pushMetricsSet.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_block_size_bytes{url=%q}`, pushURLRedacted)),
|
||||
pushDuration: pushMetricsSet.GetOrCreateHistogram(fmt.Sprintf(`metrics_push_duration_seconds{url=%q}`, pushURLRedacted)),
|
||||
pushErrors: pushMetricsSet.GetOrCreateCounter(fmt.Sprintf(`metrics_push_errors_total{url=%q}`, pushURLRedacted)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pc *pushContext) pushMetrics(ctx context.Context, writeMetrics func(w io.Writer)) error {
|
||||
bb := getBytesBuffer()
|
||||
defer putBytesBuffer(bb)
|
||||
|
||||
writeMetrics(bb)
|
||||
|
||||
if len(pc.extraLabels) > 0 {
|
||||
bbTmp := getBytesBuffer()
|
||||
bbTmp.B = append(bbTmp.B[:0], bb.B...)
|
||||
bb.B = addExtraLabels(bb.B[:0], bbTmp.B, pc.extraLabels)
|
||||
putBytesBuffer(bbTmp)
|
||||
}
|
||||
if !pc.disableCompression {
|
||||
bbTmp := getBytesBuffer()
|
||||
bbTmp.B = append(bbTmp.B[:0], bb.B...)
|
||||
bb.B = bb.B[:0]
|
||||
zw := getGzipWriter(bb)
|
||||
if _, err := zw.Write(bbTmp.B); err != nil {
|
||||
panic(fmt.Errorf("BUG: cannot write %d bytes to gzip writer: %s", len(bbTmp.B), err))
|
||||
}
|
||||
if err := zw.Close(); err != nil {
|
||||
panic(fmt.Errorf("BUG: cannot flush metrics to gzip writer: %s", err))
|
||||
}
|
||||
putGzipWriter(zw)
|
||||
putBytesBuffer(bbTmp)
|
||||
}
|
||||
|
||||
// Update metrics
|
||||
pc.pushesTotal.Inc()
|
||||
blockLen := len(bb.B)
|
||||
pc.bytesPushedTotal.Add(blockLen)
|
||||
pc.pushBlockSize.Update(float64(blockLen))
|
||||
|
||||
// Prepare the request to sent to pc.pushURL
|
||||
reqBody := bytes.NewReader(bb.B)
|
||||
req, err := http.NewRequestWithContext(ctx, pc.method, pc.pushURL.String(), reqBody)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("BUG: metrics.push: cannot initialize request for metrics push to %q: %w", pc.pushURLRedacted, err))
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
// Set the needed headers, and `Content-Type` allowed be overwrited.
|
||||
for name, values := range pc.headers {
|
||||
for _, value := range values {
|
||||
req.Header.Add(name, value)
|
||||
}
|
||||
}
|
||||
if !pc.disableCompression {
|
||||
req.Header.Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
// Perform the request
|
||||
startTime := time.Now()
|
||||
resp, err := pc.client.Do(req)
|
||||
pc.pushDuration.UpdateDuration(startTime)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return nil
|
||||
}
|
||||
pc.pushErrors.Inc()
|
||||
return fmt.Errorf("cannot push metrics to %q: %s", pc.pushURLRedacted, err)
|
||||
}
|
||||
if resp.StatusCode/100 != 2 {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
pc.pushErrors.Inc()
|
||||
return fmt.Errorf("unexpected status code in response from %q: %d; expecting 2xx; response body: %q", pc.pushURLRedacted, resp.StatusCode, body)
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
var pushMetricsSet = NewSet()
|
||||
|
||||
func writePushMetrics(w io.Writer) {
|
||||
pushMetricsSet.WritePrometheus(w)
|
||||
}
|
||||
|
||||
func addExtraLabels(dst, src []byte, extraLabels string) []byte {
|
||||
for len(src) > 0 {
|
||||
var line []byte
|
||||
n := bytes.IndexByte(src, '\n')
|
||||
if n >= 0 {
|
||||
line = src[:n]
|
||||
src = src[n+1:]
|
||||
} else {
|
||||
line = src
|
||||
src = nil
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
// Skip empy lines
|
||||
continue
|
||||
}
|
||||
if bytes.HasPrefix(line, bashBytes) {
|
||||
// Copy comments as is
|
||||
dst = append(dst, line...)
|
||||
dst = append(dst, '\n')
|
||||
continue
|
||||
}
|
||||
n = bytes.IndexByte(line, '{')
|
||||
if n >= 0 {
|
||||
dst = append(dst, line[:n+1]...)
|
||||
dst = append(dst, extraLabels...)
|
||||
dst = append(dst, ',')
|
||||
dst = append(dst, line[n+1:]...)
|
||||
} else {
|
||||
n = bytes.LastIndexByte(line, ' ')
|
||||
if n < 0 {
|
||||
panic(fmt.Errorf("BUG: missing whitespace between metric name and metric value in Prometheus text exposition line %q", line))
|
||||
}
|
||||
dst = append(dst, line[:n]...)
|
||||
dst = append(dst, '{')
|
||||
dst = append(dst, extraLabels...)
|
||||
dst = append(dst, '}')
|
||||
dst = append(dst, line[n:]...)
|
||||
}
|
||||
dst = append(dst, '\n')
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
var bashBytes = []byte("#")
|
||||
|
||||
func getBytesBuffer() *bytesBuffer {
|
||||
v := bytesBufferPool.Get()
|
||||
if v == nil {
|
||||
return &bytesBuffer{}
|
||||
}
|
||||
return v.(*bytesBuffer)
|
||||
}
|
||||
|
||||
func putBytesBuffer(bb *bytesBuffer) {
|
||||
bb.B = bb.B[:0]
|
||||
bytesBufferPool.Put(bb)
|
||||
}
|
||||
|
||||
var bytesBufferPool sync.Pool
|
||||
|
||||
type bytesBuffer struct {
|
||||
B []byte
|
||||
}
|
||||
|
||||
func (bb *bytesBuffer) Write(p []byte) (int, error) {
|
||||
bb.B = append(bb.B, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func getGzipWriter(w io.Writer) *gzip.Writer {
|
||||
v := gzipWriterPool.Get()
|
||||
if v == nil {
|
||||
return gzip.NewWriter(w)
|
||||
}
|
||||
zw := v.(*gzip.Writer)
|
||||
zw.Reset(w)
|
||||
return zw
|
||||
}
|
||||
|
||||
func putGzipWriter(zw *gzip.Writer) {
|
||||
zw.Reset(io.Discard)
|
||||
gzipWriterPool.Put(zw)
|
||||
}
|
||||
|
||||
var gzipWriterPool sync.Pool
|
||||
703
vendor/github.com/VictoriaMetrics/metrics/set.go
generated
vendored
Normal file
703
vendor/github.com/VictoriaMetrics/metrics/set.go
generated
vendored
Normal file
@@ -0,0 +1,703 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Set is a set of metrics.
|
||||
//
|
||||
// Metrics belonging to a set are exported separately from global metrics.
|
||||
//
|
||||
// Set.WritePrometheus must be called for exporting metrics from the set.
|
||||
type Set struct {
|
||||
mu sync.Mutex
|
||||
a []*namedMetric
|
||||
m map[string]*namedMetric
|
||||
summaries []*Summary
|
||||
|
||||
metricsWriters []func(w io.Writer)
|
||||
}
|
||||
|
||||
// NewSet creates new set of metrics.
|
||||
//
|
||||
// Pass the set to RegisterSet() function in order to export its metrics via global WritePrometheus() call.
|
||||
func NewSet() *Set {
|
||||
return &Set{
|
||||
m: make(map[string]*namedMetric),
|
||||
}
|
||||
}
|
||||
|
||||
// WritePrometheus writes all the metrics from s to w in Prometheus format.
|
||||
func (s *Set) WritePrometheus(w io.Writer) {
|
||||
// Collect all the metrics in in-memory buffer in order to prevent from long locking due to slow w.
|
||||
var bb bytes.Buffer
|
||||
lessFunc := func(i, j int) bool {
|
||||
// the sorting must be stable.
|
||||
// see edge cases why we can't simply do `s.a[i].name < s.a[j].name` here:
|
||||
// https://github.com/VictoriaMetrics/metrics/pull/99#issuecomment-3277072175
|
||||
|
||||
// sort by metric family name first, to group the same metric family in one place.
|
||||
fName1, fName2 := getMetricFamily(s.a[i].name), getMetricFamily(s.a[j].name)
|
||||
if fName1 != fName2 {
|
||||
return fName1 < fName2
|
||||
}
|
||||
|
||||
// Only summary and quantile(s) have different metric types.
|
||||
// Sorting by metric type will stabilize the order for summary and quantile(s).
|
||||
mType1 := s.a[i].metric.metricType()
|
||||
mType2 := s.a[j].metric.metricType()
|
||||
if mType1 != mType2 {
|
||||
return mType1 < mType2
|
||||
}
|
||||
|
||||
// lastly by metric names, which is for quantiles and histogram buckets.
|
||||
return s.a[i].name < s.a[j].name
|
||||
}
|
||||
s.mu.Lock()
|
||||
for _, sm := range s.summaries {
|
||||
sm.updateQuantiles()
|
||||
}
|
||||
if !sort.SliceIsSorted(s.a, lessFunc) {
|
||||
sort.Slice(s.a, lessFunc)
|
||||
}
|
||||
sa := append([]*namedMetric(nil), s.a...)
|
||||
metricsWriters := s.metricsWriters
|
||||
s.mu.Unlock()
|
||||
|
||||
// metricsWithMetadataBuf is used to hold marshalTo temporary, and decide whether metadata is needed.
|
||||
// it will be written to `bb` at the end and then reset for next *namedMetric in for-loop.
|
||||
var metricsWithMetadataBuf bytes.Buffer
|
||||
var prevMetricFamily string
|
||||
for _, nm := range sa {
|
||||
if !isMetadataEnabled() {
|
||||
// Call marshalTo without the global lock, since certain metric types such as Gauge
|
||||
// can call a callback, which, in turn, can try calling s.mu.Lock again.
|
||||
nm.metric.marshalTo(nm.name, &bb)
|
||||
continue
|
||||
}
|
||||
|
||||
metricsWithMetadataBuf.Reset()
|
||||
// Call marshalTo without the global lock, since certain metric types such as Gauge
|
||||
// can call a callback, which, in turn, can try calling s.mu.Lock again.
|
||||
nm.metric.marshalTo(nm.name, &metricsWithMetadataBuf)
|
||||
if metricsWithMetadataBuf.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
metricFamily := getMetricFamily(nm.name)
|
||||
if metricFamily != prevMetricFamily {
|
||||
// write metadata only once per metric family
|
||||
metricType := nm.metric.metricType()
|
||||
writeMetadata(&bb, metricFamily, metricType)
|
||||
prevMetricFamily = metricFamily
|
||||
}
|
||||
bb.Write(metricsWithMetadataBuf.Bytes())
|
||||
}
|
||||
w.Write(bb.Bytes())
|
||||
|
||||
for _, writeMetrics := range metricsWriters {
|
||||
writeMetrics(w)
|
||||
}
|
||||
}
|
||||
|
||||
// NewHistogram creates and returns new histogram in s with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewHistogram(name string) *Histogram {
|
||||
h := &Histogram{}
|
||||
s.registerMetric(name, h)
|
||||
return h
|
||||
}
|
||||
|
||||
// GetOrCreateHistogram returns registered histogram in s with the given name
|
||||
// or creates new histogram if s doesn't contain histogram with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewHistogram instead of GetOrCreateHistogram.
|
||||
func (s *Set) GetOrCreateHistogram(name string) *Histogram {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing histogram.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: &Histogram{},
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
h, ok := nm.metric.(*Histogram)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a Histogram. It is %T", name, nm.metric))
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// NewPrometheusHistogram creates and returns new PrometheusHistogram in s
|
||||
// with the given name and PrometheusHistogramDefaultBuckets.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewPrometheusHistogram(name string) *PrometheusHistogram {
|
||||
return s.NewPrometheusHistogramExt(name, PrometheusHistogramDefaultBuckets)
|
||||
}
|
||||
|
||||
// NewPrometheusHistogramExt creates and returns new PrometheusHistogram in s
|
||||
// with the given name and upperBounds.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewPrometheusHistogramExt(name string, upperBounds []float64) *PrometheusHistogram {
|
||||
h := newPrometheusHistogram(upperBounds)
|
||||
s.registerMetric(name, h)
|
||||
return h
|
||||
}
|
||||
|
||||
// GetOrCreatePrometheusHistogram returns registered prometheus histogram in s
|
||||
// with the given name or creates new histogram if s doesn't contain histogram
|
||||
// with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewPrometheusHistogram instead of GetOrCreatePrometheusHistogram.
|
||||
func (s *Set) GetOrCreatePrometheusHistogram(name string) *PrometheusHistogram {
|
||||
return s.GetOrCreatePrometheusHistogramExt(name, PrometheusHistogramDefaultBuckets)
|
||||
}
|
||||
|
||||
// GetOrCreatePrometheusHistogramExt returns registered prometheus histogram in
|
||||
// s with the given name or creates new histogram if s doesn't contain
|
||||
// histogram with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned histogram is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewPrometheusHistogramExt instead of GetOrCreatePrometheusHistogramExt.
|
||||
func (s *Set) GetOrCreatePrometheusHistogramExt(name string, upperBounds []float64) *PrometheusHistogram {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing histogram.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: newPrometheusHistogram(upperBounds),
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
h, ok := nm.metric.(*PrometheusHistogram)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a PrometheusHistogram. It is %T", name, nm.metric))
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// NewCounter registers and returns new counter with the given name in the s.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned counter is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewCounter(name string) *Counter {
|
||||
c := &Counter{}
|
||||
s.registerMetric(name, c)
|
||||
return c
|
||||
}
|
||||
|
||||
// GetOrCreateCounter returns registered counter in s with the given name
|
||||
// or creates new counter if s doesn't contain counter with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned counter is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewCounter instead of GetOrCreateCounter.
|
||||
func (s *Set) GetOrCreateCounter(name string) *Counter {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing counter.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: &Counter{},
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
c, ok := nm.metric.(*Counter)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a Counter. It is %T", name, nm.metric))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// NewFloatCounter registers and returns new FloatCounter with the given name in the s.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned FloatCounter is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewFloatCounter(name string) *FloatCounter {
|
||||
c := &FloatCounter{}
|
||||
s.registerMetric(name, c)
|
||||
return c
|
||||
}
|
||||
|
||||
// GetOrCreateFloatCounter returns registered FloatCounter in s with the given name
|
||||
// or creates new FloatCounter if s doesn't contain FloatCounter with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned FloatCounter is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewFloatCounter instead of GetOrCreateFloatCounter.
|
||||
func (s *Set) GetOrCreateFloatCounter(name string) *FloatCounter {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing counter.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: &FloatCounter{},
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
c, ok := nm.metric.(*FloatCounter)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a Counter. It is %T", name, nm.metric))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// NewGauge registers and returns gauge with the given name in s, which calls f
|
||||
// to obtain gauge value.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// f must be safe for concurrent calls.
|
||||
//
|
||||
// The returned gauge is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewGauge(name string, f func() float64) *Gauge {
|
||||
g := &Gauge{
|
||||
f: f,
|
||||
}
|
||||
s.registerMetric(name, g)
|
||||
return g
|
||||
}
|
||||
|
||||
// GetOrCreateGauge returns registered gauge with the given name in s
|
||||
// or creates new gauge if s doesn't contain gauge with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned gauge is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewGauge instead of GetOrCreateGauge.
|
||||
func (s *Set) GetOrCreateGauge(name string, f func() float64) *Gauge {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing gauge.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: &Gauge{
|
||||
f: f,
|
||||
},
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
g, ok := nm.metric.(*Gauge)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a Gauge. It is %T", name, nm.metric))
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
// NewSummary creates and returns new summary with the given name in s.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewSummary(name string) *Summary {
|
||||
return s.NewSummaryExt(name, defaultSummaryWindow, defaultSummaryQuantiles)
|
||||
}
|
||||
|
||||
// NewSummaryExt creates and returns new summary in s with the given name,
|
||||
// window and quantiles.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
func (s *Set) NewSummaryExt(name string, window time.Duration, quantiles []float64) *Summary {
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
sm := newSummary(window, quantiles)
|
||||
|
||||
s.mu.Lock()
|
||||
// defer will unlock in case of panic
|
||||
// checks in tests
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.mustRegisterLocked(name, sm, false)
|
||||
registerSummaryLocked(sm)
|
||||
s.registerSummaryQuantilesLocked(name, sm)
|
||||
s.summaries = append(s.summaries, sm)
|
||||
return sm
|
||||
}
|
||||
|
||||
// GetOrCreateSummary returns registered summary with the given name in s
|
||||
// or creates new summary if s doesn't contain summary with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewSummary instead of GetOrCreateSummary.
|
||||
func (s *Set) GetOrCreateSummary(name string) *Summary {
|
||||
return s.GetOrCreateSummaryExt(name, defaultSummaryWindow, defaultSummaryQuantiles)
|
||||
}
|
||||
|
||||
// GetOrCreateSummaryExt returns registered summary with the given name,
|
||||
// window and quantiles in s or creates new summary if s doesn't
|
||||
// contain summary with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewSummaryExt instead of GetOrCreateSummaryExt.
|
||||
func (s *Set) GetOrCreateSummaryExt(name string, window time.Duration, quantiles []float64) *Summary {
|
||||
s.mu.Lock()
|
||||
nm := s.m[name]
|
||||
s.mu.Unlock()
|
||||
if nm == nil {
|
||||
// Slow path - create and register missing summary.
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
sm := newSummary(window, quantiles)
|
||||
nmNew := &namedMetric{
|
||||
name: name,
|
||||
metric: sm,
|
||||
}
|
||||
s.mu.Lock()
|
||||
nm = s.m[name]
|
||||
if nm == nil {
|
||||
nm = nmNew
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
registerSummaryLocked(sm)
|
||||
s.registerSummaryQuantilesLocked(name, sm)
|
||||
}
|
||||
s.summaries = append(s.summaries, sm)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
sm, ok := nm.metric.(*Summary)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("BUG: metric %q isn't a Summary. It is %T", name, nm.metric))
|
||||
}
|
||||
if sm.window != window {
|
||||
panic(fmt.Errorf("BUG: invalid window requested for the summary %q; requested %s; need %s", name, window, sm.window))
|
||||
}
|
||||
if !isEqualQuantiles(sm.quantiles, quantiles) {
|
||||
panic(fmt.Errorf("BUG: invalid quantiles requested from the summary %q; requested %v; need %v", name, quantiles, sm.quantiles))
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
func (s *Set) registerSummaryQuantilesLocked(name string, sm *Summary) {
|
||||
for i, q := range sm.quantiles {
|
||||
quantileValueName := addTag(name, fmt.Sprintf(`quantile="%g"`, q))
|
||||
qv := &quantileValue{
|
||||
sm: sm,
|
||||
idx: i,
|
||||
}
|
||||
s.mustRegisterLocked(quantileValueName, qv, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set) registerMetric(name string, m metric) {
|
||||
if err := ValidateMetric(name); err != nil {
|
||||
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
|
||||
}
|
||||
s.mu.Lock()
|
||||
// defer will unlock in case of panic
|
||||
// checks in test
|
||||
defer s.mu.Unlock()
|
||||
s.mustRegisterLocked(name, m, false)
|
||||
}
|
||||
|
||||
// mustRegisterLocked registers given metric with the given name.
|
||||
//
|
||||
// Panics if the given name was already registered before.
|
||||
func (s *Set) mustRegisterLocked(name string, m metric, isAux bool) {
|
||||
nm, ok := s.m[name]
|
||||
if !ok {
|
||||
nm = &namedMetric{
|
||||
name: name,
|
||||
metric: m,
|
||||
isAux: isAux,
|
||||
}
|
||||
s.m[name] = nm
|
||||
s.a = append(s.a, nm)
|
||||
}
|
||||
if ok {
|
||||
panic(fmt.Errorf("BUG: metric %q is already registered", name))
|
||||
}
|
||||
}
|
||||
|
||||
// UnregisterMetric removes metric with the given name from s.
|
||||
//
|
||||
// True is returned if the metric has been removed.
|
||||
// False is returned if the given metric is missing in s.
|
||||
func (s *Set) UnregisterMetric(name string) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
nm, ok := s.m[name]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if nm.isAux {
|
||||
// Do not allow deleting auxiliary metrics such as summary_metric{quantile="..."}
|
||||
// Such metrics must be deleted via parent metric name, e.g. summary_metric .
|
||||
return false
|
||||
}
|
||||
return s.unregisterMetricLocked(nm)
|
||||
}
|
||||
|
||||
func (s *Set) unregisterMetricLocked(nm *namedMetric) bool {
|
||||
name := nm.name
|
||||
delete(s.m, name)
|
||||
|
||||
deleteFromList := func(metricName string) {
|
||||
for i, nm := range s.a {
|
||||
if nm.name == metricName {
|
||||
s.a = append(s.a[:i], s.a[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(fmt.Errorf("BUG: cannot find metric %q in the list of registered metrics", name))
|
||||
}
|
||||
|
||||
// remove metric from s.a
|
||||
deleteFromList(name)
|
||||
|
||||
sm, ok := nm.metric.(*Summary)
|
||||
if !ok {
|
||||
// There is no need in cleaning up non-summary metrics.
|
||||
return true
|
||||
}
|
||||
|
||||
// cleanup registry from per-quantile metrics
|
||||
for _, q := range sm.quantiles {
|
||||
quantileValueName := addTag(name, fmt.Sprintf(`quantile="%g"`, q))
|
||||
delete(s.m, quantileValueName)
|
||||
deleteFromList(quantileValueName)
|
||||
}
|
||||
|
||||
// Remove sm from s.summaries
|
||||
found := false
|
||||
for i, xsm := range s.summaries {
|
||||
if xsm == sm {
|
||||
s.summaries = append(s.summaries[:i], s.summaries[i+1:]...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
panic(fmt.Errorf("BUG: cannot find summary %q in the list of registered summaries", name))
|
||||
}
|
||||
unregisterSummary(sm)
|
||||
return true
|
||||
}
|
||||
|
||||
// UnregisterAllMetrics de-registers all metrics registered in s.
|
||||
//
|
||||
// It also de-registers writeMetrics callbacks passed to RegisterMetricsWriter.
|
||||
func (s *Set) UnregisterAllMetrics() {
|
||||
metricNames := s.ListMetricNames()
|
||||
for _, name := range metricNames {
|
||||
s.UnregisterMetric(name)
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
s.metricsWriters = nil
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// ListMetricNames returns sorted list of all the metrics in s.
|
||||
//
|
||||
// The returned list doesn't include metrics generated by metricsWriter passed to RegisterMetricsWriter.
|
||||
func (s *Set) ListMetricNames() []string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
metricNames := make([]string, 0, len(s.m))
|
||||
for _, nm := range s.m {
|
||||
if nm.isAux {
|
||||
continue
|
||||
}
|
||||
metricNames = append(metricNames, nm.name)
|
||||
}
|
||||
sort.Strings(metricNames)
|
||||
return metricNames
|
||||
}
|
||||
|
||||
// RegisterMetricsWriter registers writeMetrics callback for including metrics in the output generated by s.WritePrometheus.
|
||||
//
|
||||
// The writeMetrics callback must write metrics to w in Prometheus text exposition format without timestamps and trailing comments.
|
||||
// The last line generated by writeMetrics must end with \n.
|
||||
// See https://github.com/prometheus/docs/blob/main/content/docs/instrumenting/exposition_formats.md#text-based-format
|
||||
//
|
||||
// It is OK to reguster multiple writeMetrics callbacks - all of them will be called sequentially for gererating the output at s.WritePrometheus.
|
||||
func (s *Set) RegisterMetricsWriter(writeMetrics func(w io.Writer)) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.metricsWriters = append(s.metricsWriters, writeMetrics)
|
||||
}
|
||||
276
vendor/github.com/VictoriaMetrics/metrics/summary.go
generated
vendored
Normal file
276
vendor/github.com/VictoriaMetrics/metrics/summary.go
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/histogram"
|
||||
)
|
||||
|
||||
const defaultSummaryWindow = 5 * time.Minute
|
||||
|
||||
var defaultSummaryQuantiles = []float64{0.5, 0.9, 0.97, 0.99, 1}
|
||||
|
||||
// Summary implements summary.
|
||||
type Summary struct {
|
||||
mu sync.Mutex
|
||||
|
||||
curr *histogram.Fast
|
||||
next *histogram.Fast
|
||||
|
||||
quantiles []float64
|
||||
quantileValues []float64
|
||||
|
||||
sum float64
|
||||
count uint64
|
||||
|
||||
window time.Duration
|
||||
}
|
||||
|
||||
// NewSummary creates and returns new summary with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
func NewSummary(name string) *Summary {
|
||||
return defaultSet.NewSummary(name)
|
||||
}
|
||||
|
||||
// NewSummaryExt creates and returns new summary with the given name,
|
||||
// window and quantiles.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
func NewSummaryExt(name string, window time.Duration, quantiles []float64) *Summary {
|
||||
return defaultSet.NewSummaryExt(name, window, quantiles)
|
||||
}
|
||||
|
||||
func newSummary(window time.Duration, quantiles []float64) *Summary {
|
||||
// Make a copy of quantiles in order to prevent from their modification by the caller.
|
||||
quantiles = append([]float64{}, quantiles...)
|
||||
validateQuantiles(quantiles)
|
||||
sm := &Summary{
|
||||
curr: histogram.NewFast(),
|
||||
next: histogram.NewFast(),
|
||||
quantiles: quantiles,
|
||||
quantileValues: make([]float64, len(quantiles)),
|
||||
window: window,
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
func validateQuantiles(quantiles []float64) {
|
||||
for _, q := range quantiles {
|
||||
if q < 0 || q > 1 {
|
||||
panic(fmt.Errorf("BUG: quantile must be in the range [0..1]; got %v", q))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates the summary.
|
||||
func (sm *Summary) Update(v float64) {
|
||||
sm.mu.Lock()
|
||||
sm.curr.Update(v)
|
||||
sm.next.Update(v)
|
||||
sm.sum += v
|
||||
sm.count++
|
||||
sm.mu.Unlock()
|
||||
}
|
||||
|
||||
// UpdateDuration updates request duration based on the given startTime.
|
||||
func (sm *Summary) UpdateDuration(startTime time.Time) {
|
||||
d := time.Since(startTime).Seconds()
|
||||
sm.Update(d)
|
||||
}
|
||||
|
||||
func (sm *Summary) marshalTo(prefix string, w io.Writer) {
|
||||
// Marshal only *_sum and *_count values.
|
||||
// Quantile values should be already updated by the caller via sm.updateQuantiles() call.
|
||||
// sm.quantileValues will be marshaled later via quantileValue.marshalTo.
|
||||
sm.mu.Lock()
|
||||
sum := sm.sum
|
||||
count := sm.count
|
||||
sm.mu.Unlock()
|
||||
|
||||
if count > 0 {
|
||||
name, filters := splitMetricName(prefix)
|
||||
if float64(int64(sum)) == sum {
|
||||
// Marshal integer sum without scientific notation
|
||||
fmt.Fprintf(w, "%s_sum%s %d\n", name, filters, int64(sum))
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s_sum%s %g\n", name, filters, sum)
|
||||
}
|
||||
fmt.Fprintf(w, "%s_count%s %d\n", name, filters, count)
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *Summary) metricType() string {
|
||||
// this metric type should not be printed, because summary (sum and count)
|
||||
// of the same metric family will be printed after quantile(s).
|
||||
// If metadata is needed, the metadata from quantile(s) should be used.
|
||||
// quantile will be printed first, so its metrics type won't be printed as metadata.
|
||||
// Printing quantiles before sum and count aligns this code with Prometheus behavior.
|
||||
// See: https://github.com/VictoriaMetrics/metrics/pull/99
|
||||
return "unsupported"
|
||||
}
|
||||
|
||||
func splitMetricName(name string) (string, string) {
|
||||
n := strings.IndexByte(name, '{')
|
||||
if n < 0 {
|
||||
return name, ""
|
||||
}
|
||||
return name[:n], name[n:]
|
||||
}
|
||||
|
||||
func (sm *Summary) updateQuantiles() {
|
||||
sm.mu.Lock()
|
||||
sm.quantileValues = sm.curr.Quantiles(sm.quantileValues[:0], sm.quantiles)
|
||||
sm.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetOrCreateSummary returns registered summary with the given name
|
||||
// or creates new summary if the registry doesn't contain summary with
|
||||
// the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewSummary instead of GetOrCreateSummary.
|
||||
func GetOrCreateSummary(name string) *Summary {
|
||||
return defaultSet.GetOrCreateSummary(name)
|
||||
}
|
||||
|
||||
// GetOrCreateSummaryExt returns registered summary with the given name,
|
||||
// window and quantiles or creates new summary if the registry doesn't
|
||||
// contain summary with the given name.
|
||||
//
|
||||
// name must be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
//
|
||||
// The returned summary is safe to use from concurrent goroutines.
|
||||
//
|
||||
// Performance tip: prefer NewSummaryExt instead of GetOrCreateSummaryExt.
|
||||
func GetOrCreateSummaryExt(name string, window time.Duration, quantiles []float64) *Summary {
|
||||
return defaultSet.GetOrCreateSummaryExt(name, window, quantiles)
|
||||
}
|
||||
|
||||
func isEqualQuantiles(a, b []float64) bool {
|
||||
// Do not use relfect.DeepEqual, since it is slower than the direct comparison.
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type quantileValue struct {
|
||||
sm *Summary
|
||||
idx int
|
||||
}
|
||||
|
||||
func (qv *quantileValue) marshalTo(prefix string, w io.Writer) {
|
||||
qv.sm.mu.Lock()
|
||||
v := qv.sm.quantileValues[qv.idx]
|
||||
qv.sm.mu.Unlock()
|
||||
if !math.IsNaN(v) {
|
||||
fmt.Fprintf(w, "%s %g\n", prefix, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (qv *quantileValue) metricType() string {
|
||||
return "summary"
|
||||
}
|
||||
|
||||
func addTag(name, tag string) string {
|
||||
if len(name) == 0 || name[len(name)-1] != '}' {
|
||||
return fmt.Sprintf("%s{%s}", name, tag)
|
||||
}
|
||||
name = name[:len(name)-1]
|
||||
if len(name) == 0 {
|
||||
panic(fmt.Errorf("BUG: metric name cannot be empty"))
|
||||
}
|
||||
if name[len(name)-1] == '{' {
|
||||
// case for empty labels set metric_name{}
|
||||
return fmt.Sprintf("%s%s}", name, tag)
|
||||
}
|
||||
return fmt.Sprintf("%s,%s}", name, tag)
|
||||
}
|
||||
|
||||
func registerSummaryLocked(sm *Summary) {
|
||||
window := sm.window
|
||||
summariesLock.Lock()
|
||||
summaries[window] = append(summaries[window], sm)
|
||||
if len(summaries[window]) == 1 {
|
||||
go summariesSwapCron(window)
|
||||
}
|
||||
summariesLock.Unlock()
|
||||
}
|
||||
|
||||
func unregisterSummary(sm *Summary) {
|
||||
window := sm.window
|
||||
summariesLock.Lock()
|
||||
sms := summaries[window]
|
||||
found := false
|
||||
for i, xsm := range sms {
|
||||
if xsm == sm {
|
||||
sms = append(sms[:i], sms[i+1:]...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
panic(fmt.Errorf("BUG: cannot find registered summary %p", sm))
|
||||
}
|
||||
summaries[window] = sms
|
||||
summariesLock.Unlock()
|
||||
}
|
||||
|
||||
func summariesSwapCron(window time.Duration) {
|
||||
for {
|
||||
time.Sleep(window / 2)
|
||||
summariesLock.Lock()
|
||||
for _, sm := range summaries[window] {
|
||||
sm.mu.Lock()
|
||||
tmp := sm.curr
|
||||
sm.curr = sm.next
|
||||
sm.next = tmp
|
||||
sm.next.Reset()
|
||||
sm.mu.Unlock()
|
||||
}
|
||||
summariesLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
summaries = map[time.Duration][]*Summary{}
|
||||
summariesLock sync.Mutex
|
||||
)
|
||||
94
vendor/github.com/VictoriaMetrics/metrics/validator.go
generated
vendored
Normal file
94
vendor/github.com/VictoriaMetrics/metrics/validator.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ValidateMetric validates provided string
|
||||
// to be valid Prometheus-compatible metric with possible labels.
|
||||
// For instance,
|
||||
//
|
||||
// - foo
|
||||
// - foo{bar="baz"}
|
||||
// - foo{bar="baz",aaa="b"}
|
||||
func ValidateMetric(s string) error {
|
||||
if len(s) == 0 {
|
||||
return fmt.Errorf("metric cannot be empty")
|
||||
}
|
||||
if strings.IndexByte(s, '\n') >= 0 {
|
||||
return fmt.Errorf("metric cannot contain line breaks")
|
||||
}
|
||||
n := strings.IndexByte(s, '{')
|
||||
if n < 0 {
|
||||
return validateIdent(s)
|
||||
}
|
||||
ident := s[:n]
|
||||
s = s[n+1:]
|
||||
if err := validateIdent(ident); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(s) == 0 || s[len(s)-1] != '}' {
|
||||
return fmt.Errorf("missing closing curly brace at the end of %q", ident)
|
||||
}
|
||||
return validateTags(s[:len(s)-1])
|
||||
}
|
||||
|
||||
func validateTags(s string) error {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
n := strings.IndexByte(s, '=')
|
||||
if n < 0 {
|
||||
return fmt.Errorf("missing `=` after %q", s)
|
||||
}
|
||||
ident := s[:n]
|
||||
s = s[n+1:]
|
||||
if err := validateIdent(ident); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(s) == 0 || s[0] != '"' {
|
||||
return fmt.Errorf("missing starting `\"` for %q value; tail=%q", ident, s)
|
||||
}
|
||||
s = s[1:]
|
||||
again:
|
||||
n = strings.IndexByte(s, '"')
|
||||
if n < 0 {
|
||||
return fmt.Errorf("missing trailing `\"` for %q value; tail=%q", ident, s)
|
||||
}
|
||||
m := n
|
||||
for m > 0 && s[m-1] == '\\' {
|
||||
m--
|
||||
}
|
||||
if (n-m)%2 == 1 {
|
||||
s = s[n+1:]
|
||||
goto again
|
||||
}
|
||||
s = s[n+1:]
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !strings.HasPrefix(s, ",") {
|
||||
return fmt.Errorf("missing `,` after %q value; tail=%q", ident, s)
|
||||
}
|
||||
s = skipSpace(s[1:])
|
||||
}
|
||||
}
|
||||
|
||||
func skipSpace(s string) string {
|
||||
for len(s) > 0 && s[0] == ' ' {
|
||||
s = s[1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func validateIdent(s string) error {
|
||||
if !identRegexp.MatchString(s) {
|
||||
return fmt.Errorf("invalid identifier %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var identRegexp = regexp.MustCompile("^[a-zA-Z_:.][a-zA-Z0-9_:.]*$")
|
||||
16
vendor/github.com/valyala/fastrand/.travis.yml
generated
vendored
Normal file
16
vendor/github.com/valyala/fastrand/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
|
||||
script:
|
||||
# build test for supported platforms
|
||||
- GOOS=linux go build
|
||||
- GOOS=darwin go build
|
||||
- GOOS=freebsd go build
|
||||
- GOARCH=386 go build
|
||||
|
||||
# run tests on a standard platform
|
||||
- go test -v ./...
|
||||
|
||||
21
vendor/github.com/valyala/fastrand/LICENSE
generated
vendored
Normal file
21
vendor/github.com/valyala/fastrand/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Aliaksandr Valialkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
76
vendor/github.com/valyala/fastrand/README.md
generated
vendored
Normal file
76
vendor/github.com/valyala/fastrand/README.md
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
[](https://travis-ci.org/valyala/fastrand)
|
||||
[](http://godoc.org/github.com/valyala/fastrand)
|
||||
[](https://goreportcard.com/report/github.com/valyala/fastrand)
|
||||
|
||||
|
||||
# fastrand
|
||||
|
||||
Fast pseudorandom number generator.
|
||||
|
||||
|
||||
# Features
|
||||
|
||||
- Optimized for speed.
|
||||
- Performance scales on multiple CPUs.
|
||||
|
||||
# How does it work?
|
||||
|
||||
It abuses [sync.Pool](https://golang.org/pkg/sync/#Pool) for maintaining
|
||||
"per-CPU" pseudorandom number generators.
|
||||
|
||||
TODO: firgure out how to use real per-CPU pseudorandom number generators.
|
||||
|
||||
|
||||
# Benchmark results
|
||||
|
||||
|
||||
```
|
||||
$ GOMAXPROCS=1 go test -bench=. github.com/valyala/fastrand
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/valyala/fastrand
|
||||
BenchmarkUint32n 50000000 29.7 ns/op
|
||||
BenchmarkRNGUint32n 200000000 6.50 ns/op
|
||||
BenchmarkRNGUint32nWithLock 100000000 21.5 ns/op
|
||||
BenchmarkMathRandInt31n 50000000 31.8 ns/op
|
||||
BenchmarkMathRandRNGInt31n 100000000 17.9 ns/op
|
||||
BenchmarkMathRandRNGInt31nWithLock 50000000 30.2 ns/op
|
||||
PASS
|
||||
ok github.com/valyala/fastrand 10.634s
|
||||
```
|
||||
|
||||
```
|
||||
$ GOMAXPROCS=2 go test -bench=. github.com/valyala/fastrand
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/valyala/fastrand
|
||||
BenchmarkUint32n-2 100000000 17.6 ns/op
|
||||
BenchmarkRNGUint32n-2 500000000 3.36 ns/op
|
||||
BenchmarkRNGUint32nWithLock-2 50000000 32.0 ns/op
|
||||
BenchmarkMathRandInt31n-2 20000000 51.2 ns/op
|
||||
BenchmarkMathRandRNGInt31n-2 100000000 11.0 ns/op
|
||||
BenchmarkMathRandRNGInt31nWithLock-2 20000000 91.0 ns/op
|
||||
PASS
|
||||
ok github.com/valyala/fastrand 9.543s
|
||||
```
|
||||
|
||||
```
|
||||
$ GOMAXPROCS=4 go test -bench=. github.com/valyala/fastrand
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/valyala/fastrand
|
||||
BenchmarkUint32n-4 100000000 14.2 ns/op
|
||||
BenchmarkRNGUint32n-4 500000000 3.30 ns/op
|
||||
BenchmarkRNGUint32nWithLock-4 20000000 88.7 ns/op
|
||||
BenchmarkMathRandInt31n-4 10000000 145 ns/op
|
||||
BenchmarkMathRandRNGInt31n-4 200000000 8.35 ns/op
|
||||
BenchmarkMathRandRNGInt31nWithLock-4 20000000 102 ns/op
|
||||
PASS
|
||||
ok github.com/valyala/fastrand 11.534s
|
||||
```
|
||||
|
||||
As you can see, [fastrand.Uint32n](https://godoc.org/github.com/valyala/fastrand#Uint32n)
|
||||
scales on multiple CPUs, while [rand.Int31n](https://golang.org/pkg/math/rand/#Int31n)
|
||||
doesn't scale. Their performance is comparable on `GOMAXPROCS=1`,
|
||||
but `fastrand.Uint32n` runs 3x faster than `rand.Int31n` on `GOMAXPROCS=2`
|
||||
and 10x faster than `rand.Int31n` on `GOMAXPROCS=4`.
|
||||
79
vendor/github.com/valyala/fastrand/fastrand.go
generated
vendored
Normal file
79
vendor/github.com/valyala/fastrand/fastrand.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// Package fastrand implements fast pesudorandom number generator
|
||||
// that should scale well on multi-CPU systems.
|
||||
//
|
||||
// Use crypto/rand instead of this package for generating
|
||||
// cryptographically secure random numbers.
|
||||
package fastrand
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Uint32 returns pseudorandom uint32.
|
||||
//
|
||||
// It is safe calling this function from concurrent goroutines.
|
||||
func Uint32() uint32 {
|
||||
v := rngPool.Get()
|
||||
if v == nil {
|
||||
v = &RNG{}
|
||||
}
|
||||
r := v.(*RNG)
|
||||
x := r.Uint32()
|
||||
rngPool.Put(r)
|
||||
return x
|
||||
}
|
||||
|
||||
var rngPool sync.Pool
|
||||
|
||||
// Uint32n returns pseudorandom uint32 in the range [0..maxN).
|
||||
//
|
||||
// It is safe calling this function from concurrent goroutines.
|
||||
func Uint32n(maxN uint32) uint32 {
|
||||
x := Uint32()
|
||||
// See http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
return uint32((uint64(x) * uint64(maxN)) >> 32)
|
||||
}
|
||||
|
||||
// RNG is a pseudorandom number generator.
|
||||
//
|
||||
// It is unsafe to call RNG methods from concurrent goroutines.
|
||||
type RNG struct {
|
||||
x uint32
|
||||
}
|
||||
|
||||
// Uint32 returns pseudorandom uint32.
|
||||
//
|
||||
// It is unsafe to call this method from concurrent goroutines.
|
||||
func (r *RNG) Uint32() uint32 {
|
||||
for r.x == 0 {
|
||||
r.x = getRandomUint32()
|
||||
}
|
||||
|
||||
// See https://en.wikipedia.org/wiki/Xorshift
|
||||
x := r.x
|
||||
x ^= x << 13
|
||||
x ^= x >> 17
|
||||
x ^= x << 5
|
||||
r.x = x
|
||||
return x
|
||||
}
|
||||
|
||||
// Uint32n returns pseudorandom uint32 in the range [0..maxN).
|
||||
//
|
||||
// It is unsafe to call this method from concurrent goroutines.
|
||||
func (r *RNG) Uint32n(maxN uint32) uint32 {
|
||||
x := r.Uint32()
|
||||
// See http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
return uint32((uint64(x) * uint64(maxN)) >> 32)
|
||||
}
|
||||
|
||||
// Seed sets the r state to n.
|
||||
func (r *RNG) Seed(n uint32) {
|
||||
r.x = n
|
||||
}
|
||||
|
||||
func getRandomUint32() uint32 {
|
||||
x := time.Now().UnixNano()
|
||||
return uint32((x >> 32) ^ x)
|
||||
}
|
||||
21
vendor/github.com/valyala/histogram/LICENSE
generated
vendored
Normal file
21
vendor/github.com/valyala/histogram/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Aliaksandr Valialkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
9
vendor/github.com/valyala/histogram/README.md
generated
vendored
Normal file
9
vendor/github.com/valyala/histogram/README.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
[](http://godoc.org/github.com/valyala/histogram)
|
||||
[](https://goreportcard.com/report/github.com/valyala/histogram)
|
||||
|
||||
|
||||
# histogram
|
||||
|
||||
Fast histograms for Go.
|
||||
|
||||
See [docs](https://godoc.org/github.com/valyala/histogram).
|
||||
131
vendor/github.com/valyala/histogram/histogram.go
generated
vendored
Normal file
131
vendor/github.com/valyala/histogram/histogram.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
// Package histogram provides building blocks for fast histograms.
|
||||
package histogram
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fastrand"
|
||||
)
|
||||
|
||||
var (
|
||||
infNeg = math.Inf(-1)
|
||||
infPos = math.Inf(1)
|
||||
nan = math.NaN()
|
||||
)
|
||||
|
||||
// Fast is a fast histogram.
|
||||
//
|
||||
// It cannot be used from concurrently running goroutines without
|
||||
// external synchronization.
|
||||
type Fast struct {
|
||||
max float64
|
||||
min float64
|
||||
count uint64
|
||||
|
||||
a []float64
|
||||
tmp []float64
|
||||
rng fastrand.RNG
|
||||
}
|
||||
|
||||
// NewFast returns new fast histogram.
|
||||
func NewFast() *Fast {
|
||||
f := &Fast{}
|
||||
f.Reset()
|
||||
return f
|
||||
}
|
||||
|
||||
// Reset resets the histogram.
|
||||
func (f *Fast) Reset() {
|
||||
f.max = infNeg
|
||||
f.min = infPos
|
||||
f.count = 0
|
||||
if len(f.a) > 0 {
|
||||
f.a = f.a[:0]
|
||||
f.tmp = f.tmp[:0]
|
||||
} else {
|
||||
// Free up memory occupied by unused histogram.
|
||||
f.a = nil
|
||||
f.tmp = nil
|
||||
}
|
||||
// Reset rng state in order to get repeatable results
|
||||
// for the same sequence of values passed to Fast.Update.
|
||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1612
|
||||
f.rng.Seed(1)
|
||||
}
|
||||
|
||||
// Update updates the f with v.
|
||||
func (f *Fast) Update(v float64) {
|
||||
if v > f.max {
|
||||
f.max = v
|
||||
}
|
||||
if v < f.min {
|
||||
f.min = v
|
||||
}
|
||||
|
||||
f.count++
|
||||
if len(f.a) < maxSamples {
|
||||
f.a = append(f.a, v)
|
||||
return
|
||||
}
|
||||
if n := int(f.rng.Uint32n(uint32(f.count))); n < len(f.a) {
|
||||
f.a[n] = v
|
||||
}
|
||||
}
|
||||
|
||||
const maxSamples = 1000
|
||||
|
||||
// Quantile returns the quantile value for the given phi.
|
||||
func (f *Fast) Quantile(phi float64) float64 {
|
||||
f.tmp = append(f.tmp[:0], f.a...)
|
||||
sort.Float64s(f.tmp)
|
||||
return f.quantile(phi)
|
||||
}
|
||||
|
||||
// Quantiles appends quantile values to dst for the given phis.
|
||||
func (f *Fast) Quantiles(dst, phis []float64) []float64 {
|
||||
f.tmp = append(f.tmp[:0], f.a...)
|
||||
sort.Float64s(f.tmp)
|
||||
for _, phi := range phis {
|
||||
q := f.quantile(phi)
|
||||
dst = append(dst, q)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (f *Fast) quantile(phi float64) float64 {
|
||||
if len(f.tmp) == 0 || math.IsNaN(phi) {
|
||||
return nan
|
||||
}
|
||||
if phi <= 0 {
|
||||
return f.min
|
||||
}
|
||||
if phi >= 1 {
|
||||
return f.max
|
||||
}
|
||||
idx := uint(phi*float64(len(f.tmp)-1) + 0.5)
|
||||
if idx >= uint(len(f.tmp)) {
|
||||
idx = uint(len(f.tmp) - 1)
|
||||
}
|
||||
return f.tmp[idx]
|
||||
}
|
||||
|
||||
// GetFast returns a histogram from a pool.
|
||||
func GetFast() *Fast {
|
||||
v := fastPool.Get()
|
||||
if v == nil {
|
||||
return NewFast()
|
||||
}
|
||||
return v.(*Fast)
|
||||
}
|
||||
|
||||
// PutFast puts hf to the pool.
|
||||
//
|
||||
// hf cannot be used after this call.
|
||||
func PutFast(f *Fast) {
|
||||
f.Reset()
|
||||
fastPool.Put(f)
|
||||
}
|
||||
|
||||
var fastPool sync.Pool
|
||||
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
12
vendor/golang.org/x/sys/windows/aliases.go
generated
vendored
Normal file
12
vendor/golang.org/x/sys/windows/aliases.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && go1.9
|
||||
|
||||
package windows
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Errno = syscall.Errno
|
||||
type SysProcAttr = syscall.SysProcAttr
|
||||
416
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
Normal file
416
vendor/golang.org/x/sys/windows/dll_windows.go
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// We need to use LoadLibrary and GetProcAddress from the Go runtime, because
|
||||
// the these symbols are loaded by the system linker and are required to
|
||||
// dynamically load additional symbols. Note that in the Go runtime, these
|
||||
// return syscall.Handle and syscall.Errno, but these are the same, in fact,
|
||||
// as windows.Handle and windows.Errno, and we intend to keep these the same.
|
||||
|
||||
//go:linkname syscall_loadlibrary syscall.loadlibrary
|
||||
func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno)
|
||||
|
||||
//go:linkname syscall_getprocaddress syscall.getprocaddress
|
||||
func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno)
|
||||
|
||||
// DLLError describes reasons for DLL load failures.
|
||||
type DLLError struct {
|
||||
Err error
|
||||
ObjName string
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *DLLError) Error() string { return e.Msg }
|
||||
|
||||
func (e *DLLError) Unwrap() error { return e.Err }
|
||||
|
||||
// A DLL implements access to a single DLL.
|
||||
type DLL struct {
|
||||
Name string
|
||||
Handle Handle
|
||||
}
|
||||
|
||||
// LoadDLL loads DLL file into memory.
|
||||
//
|
||||
// Warning: using LoadDLL without an absolute path name is subject to
|
||||
// DLL preloading attacks. To safely load a system DLL, use LazyDLL
|
||||
// with System set to true, or use LoadLibraryEx directly.
|
||||
func LoadDLL(name string) (dll *DLL, err error) {
|
||||
namep, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h, e := syscall_loadlibrary(namep)
|
||||
if e != 0 {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
ObjName: name,
|
||||
Msg: "Failed to load " + name + ": " + e.Error(),
|
||||
}
|
||||
}
|
||||
d := &DLL{
|
||||
Name: name,
|
||||
Handle: h,
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// MustLoadDLL is like LoadDLL but panics if load operation failes.
|
||||
func MustLoadDLL(name string) *DLL {
|
||||
d, e := LoadDLL(name)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// FindProc searches DLL d for procedure named name and returns *Proc
|
||||
// if found. It returns an error if search fails.
|
||||
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
|
||||
namep, err := BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, e := syscall_getprocaddress(d.Handle, namep)
|
||||
if e != 0 {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
ObjName: name,
|
||||
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
|
||||
}
|
||||
}
|
||||
p := &Proc{
|
||||
Dll: d,
|
||||
Name: name,
|
||||
addr: a,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// MustFindProc is like FindProc but panics if search fails.
|
||||
func (d *DLL) MustFindProc(name string) *Proc {
|
||||
p, e := d.FindProc(name)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// FindProcByOrdinal searches DLL d for procedure by ordinal and returns *Proc
|
||||
// if found. It returns an error if search fails.
|
||||
func (d *DLL) FindProcByOrdinal(ordinal uintptr) (proc *Proc, err error) {
|
||||
a, e := GetProcAddressByOrdinal(d.Handle, ordinal)
|
||||
name := "#" + itoa(int(ordinal))
|
||||
if e != nil {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
ObjName: name,
|
||||
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
|
||||
}
|
||||
}
|
||||
p := &Proc{
|
||||
Dll: d,
|
||||
Name: name,
|
||||
addr: a,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// MustFindProcByOrdinal is like FindProcByOrdinal but panics if search fails.
|
||||
func (d *DLL) MustFindProcByOrdinal(ordinal uintptr) *Proc {
|
||||
p, e := d.FindProcByOrdinal(ordinal)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Release unloads DLL d from memory.
|
||||
func (d *DLL) Release() (err error) {
|
||||
return FreeLibrary(d.Handle)
|
||||
}
|
||||
|
||||
// A Proc implements access to a procedure inside a DLL.
|
||||
type Proc struct {
|
||||
Dll *DLL
|
||||
Name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
// Addr returns the address of the procedure represented by p.
|
||||
// The return value can be passed to Syscall to run the procedure.
|
||||
func (p *Proc) Addr() uintptr {
|
||||
return p.addr
|
||||
}
|
||||
|
||||
//go:uintptrescapes
|
||||
|
||||
// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
|
||||
// are supplied.
|
||||
//
|
||||
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||
// Callers must inspect the primary return value to decide whether an error occurred
|
||||
// (according to the semantics of the specific function being called) before consulting
|
||||
// the error. The error will be guaranteed to contain windows.Errno.
|
||||
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return syscall.Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
|
||||
case 1:
|
||||
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], 0, 0)
|
||||
case 2:
|
||||
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], 0)
|
||||
case 3:
|
||||
return syscall.Syscall(p.Addr(), uintptr(len(a)), a[0], a[1], a[2])
|
||||
case 4:
|
||||
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
|
||||
case 5:
|
||||
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
|
||||
case 6:
|
||||
return syscall.Syscall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
|
||||
case 7:
|
||||
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], 0, 0)
|
||||
case 8:
|
||||
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], 0)
|
||||
case 9:
|
||||
return syscall.Syscall9(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8])
|
||||
case 10:
|
||||
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], 0, 0)
|
||||
case 11:
|
||||
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], 0)
|
||||
case 12:
|
||||
return syscall.Syscall12(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11])
|
||||
case 13:
|
||||
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], 0, 0)
|
||||
case 14:
|
||||
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], 0)
|
||||
case 15:
|
||||
return syscall.Syscall15(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14])
|
||||
default:
|
||||
panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
|
||||
}
|
||||
}
|
||||
|
||||
// A LazyDLL implements access to a single DLL.
|
||||
// It will delay the load of the DLL until the first
|
||||
// call to its Handle method or to one of its
|
||||
// LazyProc's Addr method.
|
||||
type LazyDLL struct {
|
||||
Name string
|
||||
|
||||
// System determines whether the DLL must be loaded from the
|
||||
// Windows System directory, bypassing the normal DLL search
|
||||
// path.
|
||||
System bool
|
||||
|
||||
mu sync.Mutex
|
||||
dll *DLL // non nil once DLL is loaded
|
||||
}
|
||||
|
||||
// Load loads DLL file d.Name into memory. It returns an error if fails.
|
||||
// Load will not try to load DLL, if it is already loaded into memory.
|
||||
func (d *LazyDLL) Load() error {
|
||||
// Non-racy version of:
|
||||
// if d.dll != nil {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.dll != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// kernel32.dll is special, since it's where LoadLibraryEx comes from.
|
||||
// The kernel already special-cases its name, so it's always
|
||||
// loaded from system32.
|
||||
var dll *DLL
|
||||
var err error
|
||||
if d.Name == "kernel32.dll" {
|
||||
dll, err = LoadDLL(d.Name)
|
||||
} else {
|
||||
dll, err = loadLibraryEx(d.Name, d.System)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Non-racy version of:
|
||||
// d.dll = dll
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustLoad is like Load but panics if search fails.
|
||||
func (d *LazyDLL) mustLoad() {
|
||||
e := d.Load()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle returns d's module handle.
|
||||
func (d *LazyDLL) Handle() uintptr {
|
||||
d.mustLoad()
|
||||
return uintptr(d.dll.Handle)
|
||||
}
|
||||
|
||||
// NewProc returns a LazyProc for accessing the named procedure in the DLL d.
|
||||
func (d *LazyDLL) NewProc(name string) *LazyProc {
|
||||
return &LazyProc{l: d, Name: name}
|
||||
}
|
||||
|
||||
// NewLazyDLL creates new LazyDLL associated with DLL file.
|
||||
func NewLazyDLL(name string) *LazyDLL {
|
||||
return &LazyDLL{Name: name}
|
||||
}
|
||||
|
||||
// NewLazySystemDLL is like NewLazyDLL, but will only
|
||||
// search Windows System directory for the DLL if name is
|
||||
// a base name (like "advapi32.dll").
|
||||
func NewLazySystemDLL(name string) *LazyDLL {
|
||||
return &LazyDLL{Name: name, System: true}
|
||||
}
|
||||
|
||||
// A LazyProc implements access to a procedure inside a LazyDLL.
|
||||
// It delays the lookup until the Addr method is called.
|
||||
type LazyProc struct {
|
||||
Name string
|
||||
|
||||
mu sync.Mutex
|
||||
l *LazyDLL
|
||||
proc *Proc
|
||||
}
|
||||
|
||||
// Find searches DLL for procedure named p.Name. It returns
|
||||
// an error if search fails. Find will not search procedure,
|
||||
// if it is already found and loaded into memory.
|
||||
func (p *LazyProc) Find() error {
|
||||
// Non-racy version of:
|
||||
// if p.proc == nil {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.proc == nil {
|
||||
e := p.l.Load()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
proc, e := p.l.dll.FindProc(p.Name)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
// Non-racy version of:
|
||||
// p.proc = proc
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustFind is like Find but panics if search fails.
|
||||
func (p *LazyProc) mustFind() {
|
||||
e := p.Find()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns the address of the procedure represented by p.
|
||||
// The return value can be passed to Syscall to run the procedure.
|
||||
// It will panic if the procedure cannot be found.
|
||||
func (p *LazyProc) Addr() uintptr {
|
||||
p.mustFind()
|
||||
return p.proc.Addr()
|
||||
}
|
||||
|
||||
//go:uintptrescapes
|
||||
|
||||
// Call executes procedure p with arguments a. It will panic, if more than 15 arguments
|
||||
// are supplied. It will also panic if the procedure cannot be found.
|
||||
//
|
||||
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||
// Callers must inspect the primary return value to decide whether an error occurred
|
||||
// (according to the semantics of the specific function being called) before consulting
|
||||
// the error. The error will be guaranteed to contain windows.Errno.
|
||||
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
p.mustFind()
|
||||
return p.proc.Call(a...)
|
||||
}
|
||||
|
||||
var canDoSearchSystem32Once struct {
|
||||
sync.Once
|
||||
v bool
|
||||
}
|
||||
|
||||
func initCanDoSearchSystem32() {
|
||||
// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:
|
||||
// "Windows 7, Windows Server 2008 R2, Windows Vista, and Windows
|
||||
// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on
|
||||
// systems that have KB2533623 installed. To determine whether the
|
||||
// flags are available, use GetProcAddress to get the address of the
|
||||
// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories
|
||||
// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*
|
||||
// flags can be used with LoadLibraryEx."
|
||||
canDoSearchSystem32Once.v = (modkernel32.NewProc("AddDllDirectory").Find() == nil)
|
||||
}
|
||||
|
||||
func canDoSearchSystem32() bool {
|
||||
canDoSearchSystem32Once.Do(initCanDoSearchSystem32)
|
||||
return canDoSearchSystem32Once.v
|
||||
}
|
||||
|
||||
func isBaseName(name string) bool {
|
||||
for _, c := range name {
|
||||
if c == ':' || c == '/' || c == '\\' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// loadLibraryEx wraps the Windows LoadLibraryEx function.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
|
||||
//
|
||||
// If name is not an absolute path, LoadLibraryEx searches for the DLL
|
||||
// in a variety of automatic locations unless constrained by flags.
|
||||
// See: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx
|
||||
func loadLibraryEx(name string, system bool) (*DLL, error) {
|
||||
loadDLL := name
|
||||
var flags uintptr
|
||||
if system {
|
||||
if canDoSearchSystem32() {
|
||||
flags = LOAD_LIBRARY_SEARCH_SYSTEM32
|
||||
} else if isBaseName(name) {
|
||||
// WindowsXP or unpatched Windows machine
|
||||
// trying to load "foo.dll" out of the system
|
||||
// folder, but LoadLibraryEx doesn't support
|
||||
// that yet on their system, so emulate it.
|
||||
systemdir, err := GetSystemDirectory()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loadDLL = systemdir + "\\" + name
|
||||
}
|
||||
}
|
||||
h, err := LoadLibraryEx(loadDLL, 0, flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DLL{Name: name, Handle: h}, nil
|
||||
}
|
||||
|
||||
type errString string
|
||||
|
||||
func (s errString) Error() string { return string(s) }
|
||||
8
vendor/golang.org/x/sys/windows/empty.s
generated
vendored
Normal file
8
vendor/golang.org/x/sys/windows/empty.s
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.12
|
||||
|
||||
// This file is here to allow bodyless functions with go:linkname for Go 1.11
|
||||
// and earlier (see https://golang.org/issue/23311).
|
||||
54
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
Normal file
54
vendor/golang.org/x/sys/windows/env_windows.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Windows environment variables.
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Getenv(key string) (value string, found bool) {
|
||||
return syscall.Getenv(key)
|
||||
}
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
return syscall.Setenv(key, value)
|
||||
}
|
||||
|
||||
func Clearenv() {
|
||||
syscall.Clearenv()
|
||||
}
|
||||
|
||||
func Environ() []string {
|
||||
return syscall.Environ()
|
||||
}
|
||||
|
||||
// Returns a default environment associated with the token, rather than the current
|
||||
// process. If inheritExisting is true, then this environment also inherits the
|
||||
// environment of the current process.
|
||||
func (token Token) Environ(inheritExisting bool) (env []string, err error) {
|
||||
var block *uint16
|
||||
err = CreateEnvironmentBlock(&block, token, inheritExisting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer DestroyEnvironmentBlock(block)
|
||||
blockp := unsafe.Pointer(block)
|
||||
for {
|
||||
entry := UTF16PtrToString((*uint16)(blockp))
|
||||
if len(entry) == 0 {
|
||||
break
|
||||
}
|
||||
env = append(env, entry)
|
||||
blockp = unsafe.Add(blockp, 2*(len(entry)+1))
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
||||
20
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
Normal file
20
vendor/golang.org/x/sys/windows/eventlog.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package windows
|
||||
|
||||
const (
|
||||
EVENTLOG_SUCCESS = 0
|
||||
EVENTLOG_ERROR_TYPE = 1
|
||||
EVENTLOG_WARNING_TYPE = 2
|
||||
EVENTLOG_INFORMATION_TYPE = 4
|
||||
EVENTLOG_AUDIT_SUCCESS = 8
|
||||
EVENTLOG_AUDIT_FAILURE = 16
|
||||
)
|
||||
|
||||
//sys RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) [failretval==0] = advapi32.RegisterEventSourceW
|
||||
//sys DeregisterEventSource(handle Handle) (err error) = advapi32.DeregisterEventSource
|
||||
//sys ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) = advapi32.ReportEventW
|
||||
248
vendor/golang.org/x/sys/windows/exec_windows.go
generated
vendored
Normal file
248
vendor/golang.org/x/sys/windows/exec_windows.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Fork, exec, wait, etc.
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
errorspkg "errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// EscapeArg rewrites command line argument s as prescribed
|
||||
// in http://msdn.microsoft.com/en-us/library/ms880421.
|
||||
// This function returns "" (2 double quotes) if s is empty.
|
||||
// Alternatively, these transformations are done:
|
||||
// - every back slash (\) is doubled, but only if immediately
|
||||
// followed by double quote (");
|
||||
// - every double quote (") is escaped by back slash (\);
|
||||
// - finally, s is wrapped with double quotes (arg -> "arg"),
|
||||
// but only if there is space or tab inside s.
|
||||
func EscapeArg(s string) string {
|
||||
if len(s) == 0 {
|
||||
return `""`
|
||||
}
|
||||
n := len(s)
|
||||
hasSpace := false
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"', '\\':
|
||||
n++
|
||||
case ' ', '\t':
|
||||
hasSpace = true
|
||||
}
|
||||
}
|
||||
if hasSpace {
|
||||
n += 2 // Reserve space for quotes.
|
||||
}
|
||||
if n == len(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
qs := make([]byte, n)
|
||||
j := 0
|
||||
if hasSpace {
|
||||
qs[j] = '"'
|
||||
j++
|
||||
}
|
||||
slashes := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
default:
|
||||
slashes = 0
|
||||
qs[j] = s[i]
|
||||
case '\\':
|
||||
slashes++
|
||||
qs[j] = s[i]
|
||||
case '"':
|
||||
for ; slashes > 0; slashes-- {
|
||||
qs[j] = '\\'
|
||||
j++
|
||||
}
|
||||
qs[j] = '\\'
|
||||
j++
|
||||
qs[j] = s[i]
|
||||
}
|
||||
j++
|
||||
}
|
||||
if hasSpace {
|
||||
for ; slashes > 0; slashes-- {
|
||||
qs[j] = '\\'
|
||||
j++
|
||||
}
|
||||
qs[j] = '"'
|
||||
j++
|
||||
}
|
||||
return string(qs[:j])
|
||||
}
|
||||
|
||||
// ComposeCommandLine escapes and joins the given arguments suitable for use as a Windows command line,
|
||||
// in CreateProcess's CommandLine argument, CreateService/ChangeServiceConfig's BinaryPathName argument,
|
||||
// or any program that uses CommandLineToArgv.
|
||||
func ComposeCommandLine(args []string) string {
|
||||
if len(args) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Per https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw:
|
||||
// “This function accepts command lines that contain a program name; the
|
||||
// program name can be enclosed in quotation marks or not.”
|
||||
//
|
||||
// Unfortunately, it provides no means of escaping interior quotation marks
|
||||
// within that program name, and we have no way to report them here.
|
||||
prog := args[0]
|
||||
mustQuote := len(prog) == 0
|
||||
for i := 0; i < len(prog); i++ {
|
||||
c := prog[i]
|
||||
if c <= ' ' || (c == '"' && i == 0) {
|
||||
// Force quotes for not only the ASCII space and tab as described in the
|
||||
// MSDN article, but also ASCII control characters.
|
||||
// The documentation for CommandLineToArgvW doesn't say what happens when
|
||||
// the first argument is not a valid program name, but it empirically
|
||||
// seems to drop unquoted control characters.
|
||||
mustQuote = true
|
||||
break
|
||||
}
|
||||
}
|
||||
var commandLine []byte
|
||||
if mustQuote {
|
||||
commandLine = make([]byte, 0, len(prog)+2)
|
||||
commandLine = append(commandLine, '"')
|
||||
for i := 0; i < len(prog); i++ {
|
||||
c := prog[i]
|
||||
if c == '"' {
|
||||
// This quote would interfere with our surrounding quotes.
|
||||
// We have no way to report an error, so just strip out
|
||||
// the offending character instead.
|
||||
continue
|
||||
}
|
||||
commandLine = append(commandLine, c)
|
||||
}
|
||||
commandLine = append(commandLine, '"')
|
||||
} else {
|
||||
if len(args) == 1 {
|
||||
// args[0] is a valid command line representing itself.
|
||||
// No need to allocate a new slice or string for it.
|
||||
return prog
|
||||
}
|
||||
commandLine = []byte(prog)
|
||||
}
|
||||
|
||||
for _, arg := range args[1:] {
|
||||
commandLine = append(commandLine, ' ')
|
||||
// TODO(bcmills): since we're already appending to a slice, it would be nice
|
||||
// to avoid the intermediate allocations of EscapeArg.
|
||||
// Perhaps we can factor out an appendEscapedArg function.
|
||||
commandLine = append(commandLine, EscapeArg(arg)...)
|
||||
}
|
||||
return string(commandLine)
|
||||
}
|
||||
|
||||
// DecomposeCommandLine breaks apart its argument command line into unescaped parts using CommandLineToArgv,
|
||||
// as gathered from GetCommandLine, QUERY_SERVICE_CONFIG's BinaryPathName argument, or elsewhere that
|
||||
// command lines are passed around.
|
||||
// DecomposeCommandLine returns an error if commandLine contains NUL.
|
||||
func DecomposeCommandLine(commandLine string) ([]string, error) {
|
||||
if len(commandLine) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
utf16CommandLine, err := UTF16FromString(commandLine)
|
||||
if err != nil {
|
||||
return nil, errorspkg.New("string with NUL passed to DecomposeCommandLine")
|
||||
}
|
||||
var argc int32
|
||||
argv, err := commandLineToArgv(&utf16CommandLine[0], &argc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer LocalFree(Handle(unsafe.Pointer(argv)))
|
||||
|
||||
var args []string
|
||||
for _, p := range unsafe.Slice(argv, argc) {
|
||||
args = append(args, UTF16PtrToString(p))
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// CommandLineToArgv parses a Unicode command line string and sets
|
||||
// argc to the number of parsed arguments.
|
||||
//
|
||||
// The returned memory should be freed using a single call to LocalFree.
|
||||
//
|
||||
// Note that although the return type of CommandLineToArgv indicates 8192
|
||||
// entries of up to 8192 characters each, the actual count of parsed arguments
|
||||
// may exceed 8192, and the documentation for CommandLineToArgvW does not mention
|
||||
// any bound on the lengths of the individual argument strings.
|
||||
// (See https://go.dev/issue/63236.)
|
||||
func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err error) {
|
||||
argp, err := commandLineToArgv(cmd, argc)
|
||||
argv = (*[8192]*[8192]uint16)(unsafe.Pointer(argp))
|
||||
return argv, err
|
||||
}
|
||||
|
||||
func CloseOnExec(fd Handle) {
|
||||
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||
}
|
||||
|
||||
// FullPath retrieves the full path of the specified file.
|
||||
func FullPath(name string) (path string, err error) {
|
||||
p, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
n := uint32(100)
|
||||
for {
|
||||
buf := make([]uint16, n)
|
||||
n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n <= uint32(len(buf)) {
|
||||
return UTF16ToString(buf[:n]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewProcThreadAttributeList allocates a new ProcThreadAttributeListContainer, with the requested maximum number of attributes.
|
||||
func NewProcThreadAttributeList(maxAttrCount uint32) (*ProcThreadAttributeListContainer, error) {
|
||||
var size uintptr
|
||||
err := initializeProcThreadAttributeList(nil, maxAttrCount, 0, &size)
|
||||
if err != ERROR_INSUFFICIENT_BUFFER {
|
||||
if err == nil {
|
||||
return nil, errorspkg.New("unable to query buffer size from InitializeProcThreadAttributeList")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
alloc, err := LocalAlloc(LMEM_FIXED, uint32(size))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// size is guaranteed to be ≥1 by InitializeProcThreadAttributeList.
|
||||
al := &ProcThreadAttributeListContainer{data: (*ProcThreadAttributeList)(unsafe.Pointer(alloc))}
|
||||
err = initializeProcThreadAttributeList(al.data, maxAttrCount, 0, &size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return al, err
|
||||
}
|
||||
|
||||
// Update modifies the ProcThreadAttributeList using UpdateProcThreadAttribute.
|
||||
func (al *ProcThreadAttributeListContainer) Update(attribute uintptr, value unsafe.Pointer, size uintptr) error {
|
||||
al.pointers = append(al.pointers, value)
|
||||
return updateProcThreadAttribute(al.data, 0, attribute, value, size, nil, nil)
|
||||
}
|
||||
|
||||
// Delete frees ProcThreadAttributeList's resources.
|
||||
func (al *ProcThreadAttributeListContainer) Delete() {
|
||||
deleteProcThreadAttributeList(al.data)
|
||||
LocalFree(Handle(unsafe.Pointer(al.data)))
|
||||
al.data = nil
|
||||
al.pointers = nil
|
||||
}
|
||||
|
||||
// List returns the actual ProcThreadAttributeList to be passed to StartupInfoEx.
|
||||
func (al *ProcThreadAttributeListContainer) List() *ProcThreadAttributeList {
|
||||
return al.data
|
||||
}
|
||||
48
vendor/golang.org/x/sys/windows/memory_windows.go
generated
vendored
Normal file
48
vendor/golang.org/x/sys/windows/memory_windows.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
const (
|
||||
MEM_COMMIT = 0x00001000
|
||||
MEM_RESERVE = 0x00002000
|
||||
MEM_DECOMMIT = 0x00004000
|
||||
MEM_RELEASE = 0x00008000
|
||||
MEM_RESET = 0x00080000
|
||||
MEM_TOP_DOWN = 0x00100000
|
||||
MEM_WRITE_WATCH = 0x00200000
|
||||
MEM_PHYSICAL = 0x00400000
|
||||
MEM_RESET_UNDO = 0x01000000
|
||||
MEM_LARGE_PAGES = 0x20000000
|
||||
|
||||
PAGE_NOACCESS = 0x00000001
|
||||
PAGE_READONLY = 0x00000002
|
||||
PAGE_READWRITE = 0x00000004
|
||||
PAGE_WRITECOPY = 0x00000008
|
||||
PAGE_EXECUTE = 0x00000010
|
||||
PAGE_EXECUTE_READ = 0x00000020
|
||||
PAGE_EXECUTE_READWRITE = 0x00000040
|
||||
PAGE_EXECUTE_WRITECOPY = 0x00000080
|
||||
PAGE_GUARD = 0x00000100
|
||||
PAGE_NOCACHE = 0x00000200
|
||||
PAGE_WRITECOMBINE = 0x00000400
|
||||
PAGE_TARGETS_INVALID = 0x40000000
|
||||
PAGE_TARGETS_NO_UPDATE = 0x40000000
|
||||
|
||||
QUOTA_LIMITS_HARDWS_MIN_DISABLE = 0x00000002
|
||||
QUOTA_LIMITS_HARDWS_MIN_ENABLE = 0x00000001
|
||||
QUOTA_LIMITS_HARDWS_MAX_DISABLE = 0x00000008
|
||||
QUOTA_LIMITS_HARDWS_MAX_ENABLE = 0x00000004
|
||||
)
|
||||
|
||||
type MemoryBasicInformation struct {
|
||||
BaseAddress uintptr
|
||||
AllocationBase uintptr
|
||||
AllocationProtect uint32
|
||||
PartitionId uint16
|
||||
RegionSize uintptr
|
||||
State uint32
|
||||
Protect uint32
|
||||
Type uint32
|
||||
}
|
||||
70
vendor/golang.org/x/sys/windows/mkerrors.bash
generated
vendored
Normal file
70
vendor/golang.org/x/sys/windows/mkerrors.bash
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
winerror="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/shared/winerror.h | sort -Vr | head -n 1)"
|
||||
[[ -n $winerror ]] || { echo "Unable to find winerror.h" >&2; exit 1; }
|
||||
ntstatus="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/shared/ntstatus.h | sort -Vr | head -n 1)"
|
||||
[[ -n $ntstatus ]] || { echo "Unable to find ntstatus.h" >&2; exit 1; }
|
||||
|
||||
declare -A errors
|
||||
|
||||
{
|
||||
echo "// Code generated by 'mkerrors.bash'; DO NOT EDIT."
|
||||
echo
|
||||
echo "package windows"
|
||||
echo "import \"syscall\""
|
||||
echo "const ("
|
||||
|
||||
while read -r line; do
|
||||
unset vtype
|
||||
if [[ $line =~ ^#define\ +([A-Z0-9_]+k?)\ +([A-Z0-9_]+\()?([A-Z][A-Z0-9_]+k?)\)? ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[3]}"
|
||||
elif [[ $line =~ ^#define\ +([A-Z0-9_]+k?)\ +([A-Z0-9_]+\()?((0x)?[0-9A-Fa-f]+)L?\)? ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[3]}"
|
||||
vtype="${BASH_REMATCH[2]}"
|
||||
elif [[ $line =~ ^#define\ +([A-Z0-9_]+k?)\ +\(\(([A-Z]+)\)((0x)?[0-9A-Fa-f]+)L?\) ]]; then
|
||||
key="${BASH_REMATCH[1]}"
|
||||
value="${BASH_REMATCH[3]}"
|
||||
vtype="${BASH_REMATCH[2]}"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
[[ -n $key && -n $value ]] || continue
|
||||
[[ -z ${errors["$key"]} ]] || continue
|
||||
errors["$key"]="$value"
|
||||
if [[ -v vtype ]]; then
|
||||
if [[ $key == FACILITY_* || $key == NO_ERROR ]]; then
|
||||
vtype=""
|
||||
elif [[ $vtype == *HANDLE* || $vtype == *HRESULT* ]]; then
|
||||
vtype="Handle"
|
||||
else
|
||||
vtype="syscall.Errno"
|
||||
fi
|
||||
last_vtype="$vtype"
|
||||
else
|
||||
vtype=""
|
||||
if [[ $last_vtype == Handle && $value == NO_ERROR ]]; then
|
||||
value="S_OK"
|
||||
elif [[ $last_vtype == syscall.Errno && $value == NO_ERROR ]]; then
|
||||
value="ERROR_SUCCESS"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$key $vtype = $value"
|
||||
done < "$winerror"
|
||||
|
||||
while read -r line; do
|
||||
[[ $line =~ ^#define\ (STATUS_[^\s]+)\ +\(\(NTSTATUS\)((0x)?[0-9a-fA-F]+)L?\) ]] || continue
|
||||
echo "${BASH_REMATCH[1]} NTStatus = ${BASH_REMATCH[2]}"
|
||||
done < "$ntstatus"
|
||||
|
||||
echo ")"
|
||||
} | gofmt > "zerrors_windows.go"
|
||||
27
vendor/golang.org/x/sys/windows/mkknownfolderids.bash
generated
vendored
Normal file
27
vendor/golang.org/x/sys/windows/mkknownfolderids.bash
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright 2019 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
knownfolders="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/um/KnownFolders.h | sort -Vr | head -n 1)"
|
||||
[[ -n $knownfolders ]] || { echo "Unable to find KnownFolders.h" >&2; exit 1; }
|
||||
|
||||
{
|
||||
echo "// Code generated by 'mkknownfolderids.bash'; DO NOT EDIT."
|
||||
echo
|
||||
echo "package windows"
|
||||
echo "type KNOWNFOLDERID GUID"
|
||||
echo "var ("
|
||||
while read -r line; do
|
||||
[[ $line =~ DEFINE_KNOWN_FOLDER\((FOLDERID_[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+)\) ]] || continue
|
||||
printf "%s = &KNOWNFOLDERID{0x%08x, 0x%04x, 0x%04x, [8]byte{0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}}\n" \
|
||||
"${BASH_REMATCH[1]}" $(( "${BASH_REMATCH[2]}" )) $(( "${BASH_REMATCH[3]}" )) $(( "${BASH_REMATCH[4]}" )) \
|
||||
$(( "${BASH_REMATCH[5]}" )) $(( "${BASH_REMATCH[6]}" )) $(( "${BASH_REMATCH[7]}" )) $(( "${BASH_REMATCH[8]}" )) \
|
||||
$(( "${BASH_REMATCH[9]}" )) $(( "${BASH_REMATCH[10]}" )) $(( "${BASH_REMATCH[11]}" )) $(( "${BASH_REMATCH[12]}" ))
|
||||
done < "$knownfolders"
|
||||
echo ")"
|
||||
} | gofmt > "zknownfolderids_windows.go"
|
||||
9
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
Normal file
9
vendor/golang.org/x/sys/windows/mksyscall.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build generate
|
||||
|
||||
package windows
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go setupapi_windows.go
|
||||
30
vendor/golang.org/x/sys/windows/race.go
generated
vendored
Normal file
30
vendor/golang.org/x/sys/windows/race.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && race
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = true
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
runtime.RaceAcquire(addr)
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
runtime.RaceReleaseMerge(addr)
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceReadRange(addr, len)
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceWriteRange(addr, len)
|
||||
}
|
||||
25
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
Normal file
25
vendor/golang.org/x/sys/windows/race0.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows && !race
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = false
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
}
|
||||
1435
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
Normal file
1435
vendor/golang.org/x/sys/windows/security_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
vendor/golang.org/x/sys/windows/service.go
generated
vendored
Normal file
257
vendor/golang.org/x/sys/windows/service.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package windows
|
||||
|
||||
const (
|
||||
SC_MANAGER_CONNECT = 1
|
||||
SC_MANAGER_CREATE_SERVICE = 2
|
||||
SC_MANAGER_ENUMERATE_SERVICE = 4
|
||||
SC_MANAGER_LOCK = 8
|
||||
SC_MANAGER_QUERY_LOCK_STATUS = 16
|
||||
SC_MANAGER_MODIFY_BOOT_CONFIG = 32
|
||||
SC_MANAGER_ALL_ACCESS = 0xf003f
|
||||
)
|
||||
|
||||
const (
|
||||
SERVICE_KERNEL_DRIVER = 1
|
||||
SERVICE_FILE_SYSTEM_DRIVER = 2
|
||||
SERVICE_ADAPTER = 4
|
||||
SERVICE_RECOGNIZER_DRIVER = 8
|
||||
SERVICE_WIN32_OWN_PROCESS = 16
|
||||
SERVICE_WIN32_SHARE_PROCESS = 32
|
||||
SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS
|
||||
SERVICE_INTERACTIVE_PROCESS = 256
|
||||
SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER
|
||||
SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS
|
||||
|
||||
SERVICE_BOOT_START = 0
|
||||
SERVICE_SYSTEM_START = 1
|
||||
SERVICE_AUTO_START = 2
|
||||
SERVICE_DEMAND_START = 3
|
||||
SERVICE_DISABLED = 4
|
||||
|
||||
SERVICE_ERROR_IGNORE = 0
|
||||
SERVICE_ERROR_NORMAL = 1
|
||||
SERVICE_ERROR_SEVERE = 2
|
||||
SERVICE_ERROR_CRITICAL = 3
|
||||
|
||||
SC_STATUS_PROCESS_INFO = 0
|
||||
|
||||
SC_ACTION_NONE = 0
|
||||
SC_ACTION_RESTART = 1
|
||||
SC_ACTION_REBOOT = 2
|
||||
SC_ACTION_RUN_COMMAND = 3
|
||||
|
||||
SERVICE_STOPPED = 1
|
||||
SERVICE_START_PENDING = 2
|
||||
SERVICE_STOP_PENDING = 3
|
||||
SERVICE_RUNNING = 4
|
||||
SERVICE_CONTINUE_PENDING = 5
|
||||
SERVICE_PAUSE_PENDING = 6
|
||||
SERVICE_PAUSED = 7
|
||||
SERVICE_NO_CHANGE = 0xffffffff
|
||||
|
||||
SERVICE_ACCEPT_STOP = 1
|
||||
SERVICE_ACCEPT_PAUSE_CONTINUE = 2
|
||||
SERVICE_ACCEPT_SHUTDOWN = 4
|
||||
SERVICE_ACCEPT_PARAMCHANGE = 8
|
||||
SERVICE_ACCEPT_NETBINDCHANGE = 16
|
||||
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 32
|
||||
SERVICE_ACCEPT_POWEREVENT = 64
|
||||
SERVICE_ACCEPT_SESSIONCHANGE = 128
|
||||
SERVICE_ACCEPT_PRESHUTDOWN = 256
|
||||
|
||||
SERVICE_CONTROL_STOP = 1
|
||||
SERVICE_CONTROL_PAUSE = 2
|
||||
SERVICE_CONTROL_CONTINUE = 3
|
||||
SERVICE_CONTROL_INTERROGATE = 4
|
||||
SERVICE_CONTROL_SHUTDOWN = 5
|
||||
SERVICE_CONTROL_PARAMCHANGE = 6
|
||||
SERVICE_CONTROL_NETBINDADD = 7
|
||||
SERVICE_CONTROL_NETBINDREMOVE = 8
|
||||
SERVICE_CONTROL_NETBINDENABLE = 9
|
||||
SERVICE_CONTROL_NETBINDDISABLE = 10
|
||||
SERVICE_CONTROL_DEVICEEVENT = 11
|
||||
SERVICE_CONTROL_HARDWAREPROFILECHANGE = 12
|
||||
SERVICE_CONTROL_POWEREVENT = 13
|
||||
SERVICE_CONTROL_SESSIONCHANGE = 14
|
||||
SERVICE_CONTROL_PRESHUTDOWN = 15
|
||||
|
||||
SERVICE_ACTIVE = 1
|
||||
SERVICE_INACTIVE = 2
|
||||
SERVICE_STATE_ALL = 3
|
||||
|
||||
SERVICE_QUERY_CONFIG = 1
|
||||
SERVICE_CHANGE_CONFIG = 2
|
||||
SERVICE_QUERY_STATUS = 4
|
||||
SERVICE_ENUMERATE_DEPENDENTS = 8
|
||||
SERVICE_START = 16
|
||||
SERVICE_STOP = 32
|
||||
SERVICE_PAUSE_CONTINUE = 64
|
||||
SERVICE_INTERROGATE = 128
|
||||
SERVICE_USER_DEFINED_CONTROL = 256
|
||||
SERVICE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL
|
||||
|
||||
SERVICE_RUNS_IN_SYSTEM_PROCESS = 1
|
||||
|
||||
SERVICE_CONFIG_DESCRIPTION = 1
|
||||
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
||||
SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3
|
||||
SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
|
||||
SERVICE_CONFIG_SERVICE_SID_INFO = 5
|
||||
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6
|
||||
SERVICE_CONFIG_PRESHUTDOWN_INFO = 7
|
||||
SERVICE_CONFIG_TRIGGER_INFO = 8
|
||||
SERVICE_CONFIG_PREFERRED_NODE = 9
|
||||
SERVICE_CONFIG_LAUNCH_PROTECTED = 12
|
||||
|
||||
SERVICE_SID_TYPE_NONE = 0
|
||||
SERVICE_SID_TYPE_UNRESTRICTED = 1
|
||||
SERVICE_SID_TYPE_RESTRICTED = 2 | SERVICE_SID_TYPE_UNRESTRICTED
|
||||
|
||||
SC_ENUM_PROCESS_INFO = 0
|
||||
|
||||
SERVICE_NOTIFY_STATUS_CHANGE = 2
|
||||
SERVICE_NOTIFY_STOPPED = 0x00000001
|
||||
SERVICE_NOTIFY_START_PENDING = 0x00000002
|
||||
SERVICE_NOTIFY_STOP_PENDING = 0x00000004
|
||||
SERVICE_NOTIFY_RUNNING = 0x00000008
|
||||
SERVICE_NOTIFY_CONTINUE_PENDING = 0x00000010
|
||||
SERVICE_NOTIFY_PAUSE_PENDING = 0x00000020
|
||||
SERVICE_NOTIFY_PAUSED = 0x00000040
|
||||
SERVICE_NOTIFY_CREATED = 0x00000080
|
||||
SERVICE_NOTIFY_DELETED = 0x00000100
|
||||
SERVICE_NOTIFY_DELETE_PENDING = 0x00000200
|
||||
|
||||
SC_EVENT_DATABASE_CHANGE = 0
|
||||
SC_EVENT_PROPERTY_CHANGE = 1
|
||||
SC_EVENT_STATUS_CHANGE = 2
|
||||
|
||||
SERVICE_START_REASON_DEMAND = 0x00000001
|
||||
SERVICE_START_REASON_AUTO = 0x00000002
|
||||
SERVICE_START_REASON_TRIGGER = 0x00000004
|
||||
SERVICE_START_REASON_RESTART_ON_FAILURE = 0x00000008
|
||||
SERVICE_START_REASON_DELAYEDAUTO = 0x00000010
|
||||
|
||||
SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1
|
||||
)
|
||||
|
||||
type ENUM_SERVICE_STATUS struct {
|
||||
ServiceName *uint16
|
||||
DisplayName *uint16
|
||||
ServiceStatus SERVICE_STATUS
|
||||
}
|
||||
|
||||
type SERVICE_STATUS struct {
|
||||
ServiceType uint32
|
||||
CurrentState uint32
|
||||
ControlsAccepted uint32
|
||||
Win32ExitCode uint32
|
||||
ServiceSpecificExitCode uint32
|
||||
CheckPoint uint32
|
||||
WaitHint uint32
|
||||
}
|
||||
|
||||
type SERVICE_TABLE_ENTRY struct {
|
||||
ServiceName *uint16
|
||||
ServiceProc uintptr
|
||||
}
|
||||
|
||||
type QUERY_SERVICE_CONFIG struct {
|
||||
ServiceType uint32
|
||||
StartType uint32
|
||||
ErrorControl uint32
|
||||
BinaryPathName *uint16
|
||||
LoadOrderGroup *uint16
|
||||
TagId uint32
|
||||
Dependencies *uint16
|
||||
ServiceStartName *uint16
|
||||
DisplayName *uint16
|
||||
}
|
||||
|
||||
type SERVICE_DESCRIPTION struct {
|
||||
Description *uint16
|
||||
}
|
||||
|
||||
type SERVICE_DELAYED_AUTO_START_INFO struct {
|
||||
IsDelayedAutoStartUp uint32
|
||||
}
|
||||
|
||||
type SERVICE_STATUS_PROCESS struct {
|
||||
ServiceType uint32
|
||||
CurrentState uint32
|
||||
ControlsAccepted uint32
|
||||
Win32ExitCode uint32
|
||||
ServiceSpecificExitCode uint32
|
||||
CheckPoint uint32
|
||||
WaitHint uint32
|
||||
ProcessId uint32
|
||||
ServiceFlags uint32
|
||||
}
|
||||
|
||||
type ENUM_SERVICE_STATUS_PROCESS struct {
|
||||
ServiceName *uint16
|
||||
DisplayName *uint16
|
||||
ServiceStatusProcess SERVICE_STATUS_PROCESS
|
||||
}
|
||||
|
||||
type SERVICE_NOTIFY struct {
|
||||
Version uint32
|
||||
NotifyCallback uintptr
|
||||
Context uintptr
|
||||
NotificationStatus uint32
|
||||
ServiceStatus SERVICE_STATUS_PROCESS
|
||||
NotificationTriggered uint32
|
||||
ServiceNames *uint16
|
||||
}
|
||||
|
||||
type SERVICE_FAILURE_ACTIONS struct {
|
||||
ResetPeriod uint32
|
||||
RebootMsg *uint16
|
||||
Command *uint16
|
||||
ActionsCount uint32
|
||||
Actions *SC_ACTION
|
||||
}
|
||||
|
||||
type SERVICE_FAILURE_ACTIONS_FLAG struct {
|
||||
FailureActionsOnNonCrashFailures int32
|
||||
}
|
||||
|
||||
type SC_ACTION struct {
|
||||
Type uint32
|
||||
Delay uint32
|
||||
}
|
||||
|
||||
type QUERY_SERVICE_LOCK_STATUS struct {
|
||||
IsLocked uint32
|
||||
LockOwner *uint16
|
||||
LockDuration uint32
|
||||
}
|
||||
|
||||
//sys OpenSCManager(machineName *uint16, databaseName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenSCManagerW
|
||||
//sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
|
||||
//sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW
|
||||
//sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
|
||||
//sys DeleteService(service Handle) (err error) = advapi32.DeleteService
|
||||
//sys StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
|
||||
//sys QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus
|
||||
//sys QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceLockStatusW
|
||||
//sys ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService
|
||||
//sys StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
|
||||
//sys SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus
|
||||
//sys ChangeServiceConfig(service Handle, serviceType uint32, startType uint32, errorControl uint32, binaryPathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16, displayName *uint16) (err error) = advapi32.ChangeServiceConfigW
|
||||
//sys QueryServiceConfig(service Handle, serviceConfig *QUERY_SERVICE_CONFIG, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfigW
|
||||
//sys ChangeServiceConfig2(service Handle, infoLevel uint32, info *byte) (err error) = advapi32.ChangeServiceConfig2W
|
||||
//sys QueryServiceConfig2(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceConfig2W
|
||||
//sys EnumServicesStatusEx(mgr Handle, infoLevel uint32, serviceType uint32, serviceState uint32, services *byte, bufSize uint32, bytesNeeded *uint32, servicesReturned *uint32, resumeHandle *uint32, groupName *uint16) (err error) = advapi32.EnumServicesStatusExW
|
||||
//sys QueryServiceStatusEx(service Handle, infoLevel uint32, buff *byte, buffSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceStatusEx
|
||||
//sys NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) = advapi32.NotifyServiceStatusChangeW
|
||||
//sys SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
|
||||
//sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
|
||||
//sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW
|
||||
//sys QueryServiceDynamicInformation(service Handle, infoLevel uint32, dynamicInfo unsafe.Pointer) (err error) = advapi32.QueryServiceDynamicInformation?
|
||||
//sys EnumDependentServices(service Handle, activityState uint32, services *ENUM_SERVICE_STATUS, buffSize uint32, bytesNeeded *uint32, servicesReturned *uint32) (err error) = advapi32.EnumDependentServicesW
|
||||
1425
vendor/golang.org/x/sys/windows/setupapi_windows.go
generated
vendored
Normal file
1425
vendor/golang.org/x/sys/windows/setupapi_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
vendor/golang.org/x/sys/windows/str.go
generated
vendored
Normal file
22
vendor/golang.org/x/sys/windows/str.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package windows
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + itoa(-val)
|
||||
}
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
||||
104
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
Normal file
104
vendor/golang.org/x/sys/windows/syscall.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
// Package windows contains an interface to the low-level operating system
|
||||
// primitives. OS details vary depending on the underlying system, and
|
||||
// by default, godoc will display the OS-specific documentation for the current
|
||||
// system. If you want godoc to display syscall documentation for another
|
||||
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||
// to freebsd and $GOARCH to arm.
|
||||
//
|
||||
// The primary use of this package is inside other packages that provide a more
|
||||
// portable interface to the system, such as "os", "time" and "net". Use
|
||||
// those packages rather than this one if you can.
|
||||
//
|
||||
// For details of the functions and data types in this package consult
|
||||
// the manuals for the appropriate operating system.
|
||||
//
|
||||
// These calls return err == nil to indicate success; otherwise
|
||||
// err represents an operating system error describing the failure and
|
||||
// holds a value of type syscall.Errno.
|
||||
package windows // import "golang.org/x/sys/windows"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
// containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, syscall.EINVAL).
|
||||
func ByteSliceFromString(s string) ([]byte, error) {
|
||||
if strings.IndexByte(s, 0) != -1 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
a := make([]byte, len(s)+1)
|
||||
copy(a, s)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||
// bytes containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, syscall.EINVAL).
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
a, err := ByteSliceFromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &a[0], nil
|
||||
}
|
||||
|
||||
// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
|
||||
// bytes after the NUL removed.
|
||||
func ByteSliceToString(s []byte) string {
|
||||
if i := bytes.IndexByte(s, 0); i != -1 {
|
||||
s = s[:i]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
|
||||
// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
|
||||
// at a zero byte; if the zero byte is not present, the program may crash.
|
||||
func BytePtrToString(p *byte) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if *p == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Find NUL terminator.
|
||||
n := 0
|
||||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
// See mksyscall.pl.
|
||||
var _zero uintptr
|
||||
|
||||
func (ts *Timespec) Unix() (sec int64, nsec int64) {
|
||||
return int64(ts.Sec), int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Unix() (sec int64, nsec int64) {
|
||||
return int64(tv.Sec), int64(tv.Usec) * 1000
|
||||
}
|
||||
|
||||
func (ts *Timespec) Nano() int64 {
|
||||
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Nano() int64 {
|
||||
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
|
||||
}
|
||||
1836
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
Normal file
1836
vendor/golang.org/x/sys/windows/syscall_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3382
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
Normal file
3382
vendor/golang.org/x/sys/windows/types_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
35
vendor/golang.org/x/sys/windows/types_windows_386.go
generated
vendored
Normal file
35
vendor/golang.org/x/sys/windows/types_windows_386.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
type WSAData struct {
|
||||
Version uint16
|
||||
HighVersion uint16
|
||||
Description [WSADESCRIPTION_LEN + 1]byte
|
||||
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||
MaxSockets uint16
|
||||
MaxUdpDg uint16
|
||||
VendorInfo *byte
|
||||
}
|
||||
|
||||
type Servent struct {
|
||||
Name *byte
|
||||
Aliases **byte
|
||||
Port uint16
|
||||
Proto *byte
|
||||
}
|
||||
|
||||
type JOBOBJECT_BASIC_LIMIT_INFORMATION struct {
|
||||
PerProcessUserTimeLimit int64
|
||||
PerJobUserTimeLimit int64
|
||||
LimitFlags uint32
|
||||
MinimumWorkingSetSize uintptr
|
||||
MaximumWorkingSetSize uintptr
|
||||
ActiveProcessLimit uint32
|
||||
Affinity uintptr
|
||||
PriorityClass uint32
|
||||
SchedulingClass uint32
|
||||
_ uint32 // pad to 8 byte boundary
|
||||
}
|
||||
34
vendor/golang.org/x/sys/windows/types_windows_amd64.go
generated
vendored
Normal file
34
vendor/golang.org/x/sys/windows/types_windows_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
type WSAData struct {
|
||||
Version uint16
|
||||
HighVersion uint16
|
||||
MaxSockets uint16
|
||||
MaxUdpDg uint16
|
||||
VendorInfo *byte
|
||||
Description [WSADESCRIPTION_LEN + 1]byte
|
||||
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||
}
|
||||
|
||||
type Servent struct {
|
||||
Name *byte
|
||||
Aliases **byte
|
||||
Proto *byte
|
||||
Port uint16
|
||||
}
|
||||
|
||||
type JOBOBJECT_BASIC_LIMIT_INFORMATION struct {
|
||||
PerProcessUserTimeLimit int64
|
||||
PerJobUserTimeLimit int64
|
||||
LimitFlags uint32
|
||||
MinimumWorkingSetSize uintptr
|
||||
MaximumWorkingSetSize uintptr
|
||||
ActiveProcessLimit uint32
|
||||
Affinity uintptr
|
||||
PriorityClass uint32
|
||||
SchedulingClass uint32
|
||||
}
|
||||
35
vendor/golang.org/x/sys/windows/types_windows_arm.go
generated
vendored
Normal file
35
vendor/golang.org/x/sys/windows/types_windows_arm.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
type WSAData struct {
|
||||
Version uint16
|
||||
HighVersion uint16
|
||||
Description [WSADESCRIPTION_LEN + 1]byte
|
||||
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||
MaxSockets uint16
|
||||
MaxUdpDg uint16
|
||||
VendorInfo *byte
|
||||
}
|
||||
|
||||
type Servent struct {
|
||||
Name *byte
|
||||
Aliases **byte
|
||||
Port uint16
|
||||
Proto *byte
|
||||
}
|
||||
|
||||
type JOBOBJECT_BASIC_LIMIT_INFORMATION struct {
|
||||
PerProcessUserTimeLimit int64
|
||||
PerJobUserTimeLimit int64
|
||||
LimitFlags uint32
|
||||
MinimumWorkingSetSize uintptr
|
||||
MaximumWorkingSetSize uintptr
|
||||
ActiveProcessLimit uint32
|
||||
Affinity uintptr
|
||||
PriorityClass uint32
|
||||
SchedulingClass uint32
|
||||
_ uint32 // pad to 8 byte boundary
|
||||
}
|
||||
34
vendor/golang.org/x/sys/windows/types_windows_arm64.go
generated
vendored
Normal file
34
vendor/golang.org/x/sys/windows/types_windows_arm64.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package windows
|
||||
|
||||
type WSAData struct {
|
||||
Version uint16
|
||||
HighVersion uint16
|
||||
MaxSockets uint16
|
||||
MaxUdpDg uint16
|
||||
VendorInfo *byte
|
||||
Description [WSADESCRIPTION_LEN + 1]byte
|
||||
SystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||
}
|
||||
|
||||
type Servent struct {
|
||||
Name *byte
|
||||
Aliases **byte
|
||||
Proto *byte
|
||||
Port uint16
|
||||
}
|
||||
|
||||
type JOBOBJECT_BASIC_LIMIT_INFORMATION struct {
|
||||
PerProcessUserTimeLimit int64
|
||||
PerJobUserTimeLimit int64
|
||||
LimitFlags uint32
|
||||
MinimumWorkingSetSize uintptr
|
||||
MaximumWorkingSetSize uintptr
|
||||
ActiveProcessLimit uint32
|
||||
Affinity uintptr
|
||||
PriorityClass uint32
|
||||
SchedulingClass uint32
|
||||
}
|
||||
9468
vendor/golang.org/x/sys/windows/zerrors_windows.go
generated
vendored
Normal file
9468
vendor/golang.org/x/sys/windows/zerrors_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
149
vendor/golang.org/x/sys/windows/zknownfolderids_windows.go
generated
vendored
Normal file
149
vendor/golang.org/x/sys/windows/zknownfolderids_windows.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// Code generated by 'mkknownfolderids.bash'; DO NOT EDIT.
|
||||
|
||||
package windows
|
||||
|
||||
type KNOWNFOLDERID GUID
|
||||
|
||||
var (
|
||||
FOLDERID_NetworkFolder = &KNOWNFOLDERID{0xd20beec4, 0x5ca8, 0x4905, [8]byte{0xae, 0x3b, 0xbf, 0x25, 0x1e, 0xa0, 0x9b, 0x53}}
|
||||
FOLDERID_ComputerFolder = &KNOWNFOLDERID{0x0ac0837c, 0xbbf8, 0x452a, [8]byte{0x85, 0x0d, 0x79, 0xd0, 0x8e, 0x66, 0x7c, 0xa7}}
|
||||
FOLDERID_InternetFolder = &KNOWNFOLDERID{0x4d9f7874, 0x4e0c, 0x4904, [8]byte{0x96, 0x7b, 0x40, 0xb0, 0xd2, 0x0c, 0x3e, 0x4b}}
|
||||
FOLDERID_ControlPanelFolder = &KNOWNFOLDERID{0x82a74aeb, 0xaeb4, 0x465c, [8]byte{0xa0, 0x14, 0xd0, 0x97, 0xee, 0x34, 0x6d, 0x63}}
|
||||
FOLDERID_PrintersFolder = &KNOWNFOLDERID{0x76fc4e2d, 0xd6ad, 0x4519, [8]byte{0xa6, 0x63, 0x37, 0xbd, 0x56, 0x06, 0x81, 0x85}}
|
||||
FOLDERID_SyncManagerFolder = &KNOWNFOLDERID{0x43668bf8, 0xc14e, 0x49b2, [8]byte{0x97, 0xc9, 0x74, 0x77, 0x84, 0xd7, 0x84, 0xb7}}
|
||||
FOLDERID_SyncSetupFolder = &KNOWNFOLDERID{0x0f214138, 0xb1d3, 0x4a90, [8]byte{0xbb, 0xa9, 0x27, 0xcb, 0xc0, 0xc5, 0x38, 0x9a}}
|
||||
FOLDERID_ConflictFolder = &KNOWNFOLDERID{0x4bfefb45, 0x347d, 0x4006, [8]byte{0xa5, 0xbe, 0xac, 0x0c, 0xb0, 0x56, 0x71, 0x92}}
|
||||
FOLDERID_SyncResultsFolder = &KNOWNFOLDERID{0x289a9a43, 0xbe44, 0x4057, [8]byte{0xa4, 0x1b, 0x58, 0x7a, 0x76, 0xd7, 0xe7, 0xf9}}
|
||||
FOLDERID_RecycleBinFolder = &KNOWNFOLDERID{0xb7534046, 0x3ecb, 0x4c18, [8]byte{0xbe, 0x4e, 0x64, 0xcd, 0x4c, 0xb7, 0xd6, 0xac}}
|
||||
FOLDERID_ConnectionsFolder = &KNOWNFOLDERID{0x6f0cd92b, 0x2e97, 0x45d1, [8]byte{0x88, 0xff, 0xb0, 0xd1, 0x86, 0xb8, 0xde, 0xdd}}
|
||||
FOLDERID_Fonts = &KNOWNFOLDERID{0xfd228cb7, 0xae11, 0x4ae3, [8]byte{0x86, 0x4c, 0x16, 0xf3, 0x91, 0x0a, 0xb8, 0xfe}}
|
||||
FOLDERID_Desktop = &KNOWNFOLDERID{0xb4bfcc3a, 0xdb2c, 0x424c, [8]byte{0xb0, 0x29, 0x7f, 0xe9, 0x9a, 0x87, 0xc6, 0x41}}
|
||||
FOLDERID_Startup = &KNOWNFOLDERID{0xb97d20bb, 0xf46a, 0x4c97, [8]byte{0xba, 0x10, 0x5e, 0x36, 0x08, 0x43, 0x08, 0x54}}
|
||||
FOLDERID_Programs = &KNOWNFOLDERID{0xa77f5d77, 0x2e2b, 0x44c3, [8]byte{0xa6, 0xa2, 0xab, 0xa6, 0x01, 0x05, 0x4a, 0x51}}
|
||||
FOLDERID_StartMenu = &KNOWNFOLDERID{0x625b53c3, 0xab48, 0x4ec1, [8]byte{0xba, 0x1f, 0xa1, 0xef, 0x41, 0x46, 0xfc, 0x19}}
|
||||
FOLDERID_Recent = &KNOWNFOLDERID{0xae50c081, 0xebd2, 0x438a, [8]byte{0x86, 0x55, 0x8a, 0x09, 0x2e, 0x34, 0x98, 0x7a}}
|
||||
FOLDERID_SendTo = &KNOWNFOLDERID{0x8983036c, 0x27c0, 0x404b, [8]byte{0x8f, 0x08, 0x10, 0x2d, 0x10, 0xdc, 0xfd, 0x74}}
|
||||
FOLDERID_Documents = &KNOWNFOLDERID{0xfdd39ad0, 0x238f, 0x46af, [8]byte{0xad, 0xb4, 0x6c, 0x85, 0x48, 0x03, 0x69, 0xc7}}
|
||||
FOLDERID_Favorites = &KNOWNFOLDERID{0x1777f761, 0x68ad, 0x4d8a, [8]byte{0x87, 0xbd, 0x30, 0xb7, 0x59, 0xfa, 0x33, 0xdd}}
|
||||
FOLDERID_NetHood = &KNOWNFOLDERID{0xc5abbf53, 0xe17f, 0x4121, [8]byte{0x89, 0x00, 0x86, 0x62, 0x6f, 0xc2, 0xc9, 0x73}}
|
||||
FOLDERID_PrintHood = &KNOWNFOLDERID{0x9274bd8d, 0xcfd1, 0x41c3, [8]byte{0xb3, 0x5e, 0xb1, 0x3f, 0x55, 0xa7, 0x58, 0xf4}}
|
||||
FOLDERID_Templates = &KNOWNFOLDERID{0xa63293e8, 0x664e, 0x48db, [8]byte{0xa0, 0x79, 0xdf, 0x75, 0x9e, 0x05, 0x09, 0xf7}}
|
||||
FOLDERID_CommonStartup = &KNOWNFOLDERID{0x82a5ea35, 0xd9cd, 0x47c5, [8]byte{0x96, 0x29, 0xe1, 0x5d, 0x2f, 0x71, 0x4e, 0x6e}}
|
||||
FOLDERID_CommonPrograms = &KNOWNFOLDERID{0x0139d44e, 0x6afe, 0x49f2, [8]byte{0x86, 0x90, 0x3d, 0xaf, 0xca, 0xe6, 0xff, 0xb8}}
|
||||
FOLDERID_CommonStartMenu = &KNOWNFOLDERID{0xa4115719, 0xd62e, 0x491d, [8]byte{0xaa, 0x7c, 0xe7, 0x4b, 0x8b, 0xe3, 0xb0, 0x67}}
|
||||
FOLDERID_PublicDesktop = &KNOWNFOLDERID{0xc4aa340d, 0xf20f, 0x4863, [8]byte{0xaf, 0xef, 0xf8, 0x7e, 0xf2, 0xe6, 0xba, 0x25}}
|
||||
FOLDERID_ProgramData = &KNOWNFOLDERID{0x62ab5d82, 0xfdc1, 0x4dc3, [8]byte{0xa9, 0xdd, 0x07, 0x0d, 0x1d, 0x49, 0x5d, 0x97}}
|
||||
FOLDERID_CommonTemplates = &KNOWNFOLDERID{0xb94237e7, 0x57ac, 0x4347, [8]byte{0x91, 0x51, 0xb0, 0x8c, 0x6c, 0x32, 0xd1, 0xf7}}
|
||||
FOLDERID_PublicDocuments = &KNOWNFOLDERID{0xed4824af, 0xdce4, 0x45a8, [8]byte{0x81, 0xe2, 0xfc, 0x79, 0x65, 0x08, 0x36, 0x34}}
|
||||
FOLDERID_RoamingAppData = &KNOWNFOLDERID{0x3eb685db, 0x65f9, 0x4cf6, [8]byte{0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}}
|
||||
FOLDERID_LocalAppData = &KNOWNFOLDERID{0xf1b32785, 0x6fba, 0x4fcf, [8]byte{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91}}
|
||||
FOLDERID_LocalAppDataLow = &KNOWNFOLDERID{0xa520a1a4, 0x1780, 0x4ff6, [8]byte{0xbd, 0x18, 0x16, 0x73, 0x43, 0xc5, 0xaf, 0x16}}
|
||||
FOLDERID_InternetCache = &KNOWNFOLDERID{0x352481e8, 0x33be, 0x4251, [8]byte{0xba, 0x85, 0x60, 0x07, 0xca, 0xed, 0xcf, 0x9d}}
|
||||
FOLDERID_Cookies = &KNOWNFOLDERID{0x2b0f765d, 0xc0e9, 0x4171, [8]byte{0x90, 0x8e, 0x08, 0xa6, 0x11, 0xb8, 0x4f, 0xf6}}
|
||||
FOLDERID_History = &KNOWNFOLDERID{0xd9dc8a3b, 0xb784, 0x432e, [8]byte{0xa7, 0x81, 0x5a, 0x11, 0x30, 0xa7, 0x59, 0x63}}
|
||||
FOLDERID_System = &KNOWNFOLDERID{0x1ac14e77, 0x02e7, 0x4e5d, [8]byte{0xb7, 0x44, 0x2e, 0xb1, 0xae, 0x51, 0x98, 0xb7}}
|
||||
FOLDERID_SystemX86 = &KNOWNFOLDERID{0xd65231b0, 0xb2f1, 0x4857, [8]byte{0xa4, 0xce, 0xa8, 0xe7, 0xc6, 0xea, 0x7d, 0x27}}
|
||||
FOLDERID_Windows = &KNOWNFOLDERID{0xf38bf404, 0x1d43, 0x42f2, [8]byte{0x93, 0x05, 0x67, 0xde, 0x0b, 0x28, 0xfc, 0x23}}
|
||||
FOLDERID_Profile = &KNOWNFOLDERID{0x5e6c858f, 0x0e22, 0x4760, [8]byte{0x9a, 0xfe, 0xea, 0x33, 0x17, 0xb6, 0x71, 0x73}}
|
||||
FOLDERID_Pictures = &KNOWNFOLDERID{0x33e28130, 0x4e1e, 0x4676, [8]byte{0x83, 0x5a, 0x98, 0x39, 0x5c, 0x3b, 0xc3, 0xbb}}
|
||||
FOLDERID_ProgramFilesX86 = &KNOWNFOLDERID{0x7c5a40ef, 0xa0fb, 0x4bfc, [8]byte{0x87, 0x4a, 0xc0, 0xf2, 0xe0, 0xb9, 0xfa, 0x8e}}
|
||||
FOLDERID_ProgramFilesCommonX86 = &KNOWNFOLDERID{0xde974d24, 0xd9c6, 0x4d3e, [8]byte{0xbf, 0x91, 0xf4, 0x45, 0x51, 0x20, 0xb9, 0x17}}
|
||||
FOLDERID_ProgramFilesX64 = &KNOWNFOLDERID{0x6d809377, 0x6af0, 0x444b, [8]byte{0x89, 0x57, 0xa3, 0x77, 0x3f, 0x02, 0x20, 0x0e}}
|
||||
FOLDERID_ProgramFilesCommonX64 = &KNOWNFOLDERID{0x6365d5a7, 0x0f0d, 0x45e5, [8]byte{0x87, 0xf6, 0x0d, 0xa5, 0x6b, 0x6a, 0x4f, 0x7d}}
|
||||
FOLDERID_ProgramFiles = &KNOWNFOLDERID{0x905e63b6, 0xc1bf, 0x494e, [8]byte{0xb2, 0x9c, 0x65, 0xb7, 0x32, 0xd3, 0xd2, 0x1a}}
|
||||
FOLDERID_ProgramFilesCommon = &KNOWNFOLDERID{0xf7f1ed05, 0x9f6d, 0x47a2, [8]byte{0xaa, 0xae, 0x29, 0xd3, 0x17, 0xc6, 0xf0, 0x66}}
|
||||
FOLDERID_UserProgramFiles = &KNOWNFOLDERID{0x5cd7aee2, 0x2219, 0x4a67, [8]byte{0xb8, 0x5d, 0x6c, 0x9c, 0xe1, 0x56, 0x60, 0xcb}}
|
||||
FOLDERID_UserProgramFilesCommon = &KNOWNFOLDERID{0xbcbd3057, 0xca5c, 0x4622, [8]byte{0xb4, 0x2d, 0xbc, 0x56, 0xdb, 0x0a, 0xe5, 0x16}}
|
||||
FOLDERID_AdminTools = &KNOWNFOLDERID{0x724ef170, 0xa42d, 0x4fef, [8]byte{0x9f, 0x26, 0xb6, 0x0e, 0x84, 0x6f, 0xba, 0x4f}}
|
||||
FOLDERID_CommonAdminTools = &KNOWNFOLDERID{0xd0384e7d, 0xbac3, 0x4797, [8]byte{0x8f, 0x14, 0xcb, 0xa2, 0x29, 0xb3, 0x92, 0xb5}}
|
||||
FOLDERID_Music = &KNOWNFOLDERID{0x4bd8d571, 0x6d19, 0x48d3, [8]byte{0xbe, 0x97, 0x42, 0x22, 0x20, 0x08, 0x0e, 0x43}}
|
||||
FOLDERID_Videos = &KNOWNFOLDERID{0x18989b1d, 0x99b5, 0x455b, [8]byte{0x84, 0x1c, 0xab, 0x7c, 0x74, 0xe4, 0xdd, 0xfc}}
|
||||
FOLDERID_Ringtones = &KNOWNFOLDERID{0xc870044b, 0xf49e, 0x4126, [8]byte{0xa9, 0xc3, 0xb5, 0x2a, 0x1f, 0xf4, 0x11, 0xe8}}
|
||||
FOLDERID_PublicPictures = &KNOWNFOLDERID{0xb6ebfb86, 0x6907, 0x413c, [8]byte{0x9a, 0xf7, 0x4f, 0xc2, 0xab, 0xf0, 0x7c, 0xc5}}
|
||||
FOLDERID_PublicMusic = &KNOWNFOLDERID{0x3214fab5, 0x9757, 0x4298, [8]byte{0xbb, 0x61, 0x92, 0xa9, 0xde, 0xaa, 0x44, 0xff}}
|
||||
FOLDERID_PublicVideos = &KNOWNFOLDERID{0x2400183a, 0x6185, 0x49fb, [8]byte{0xa2, 0xd8, 0x4a, 0x39, 0x2a, 0x60, 0x2b, 0xa3}}
|
||||
FOLDERID_PublicRingtones = &KNOWNFOLDERID{0xe555ab60, 0x153b, 0x4d17, [8]byte{0x9f, 0x04, 0xa5, 0xfe, 0x99, 0xfc, 0x15, 0xec}}
|
||||
FOLDERID_ResourceDir = &KNOWNFOLDERID{0x8ad10c31, 0x2adb, 0x4296, [8]byte{0xa8, 0xf7, 0xe4, 0x70, 0x12, 0x32, 0xc9, 0x72}}
|
||||
FOLDERID_LocalizedResourcesDir = &KNOWNFOLDERID{0x2a00375e, 0x224c, 0x49de, [8]byte{0xb8, 0xd1, 0x44, 0x0d, 0xf7, 0xef, 0x3d, 0xdc}}
|
||||
FOLDERID_CommonOEMLinks = &KNOWNFOLDERID{0xc1bae2d0, 0x10df, 0x4334, [8]byte{0xbe, 0xdd, 0x7a, 0xa2, 0x0b, 0x22, 0x7a, 0x9d}}
|
||||
FOLDERID_CDBurning = &KNOWNFOLDERID{0x9e52ab10, 0xf80d, 0x49df, [8]byte{0xac, 0xb8, 0x43, 0x30, 0xf5, 0x68, 0x78, 0x55}}
|
||||
FOLDERID_UserProfiles = &KNOWNFOLDERID{0x0762d272, 0xc50a, 0x4bb0, [8]byte{0xa3, 0x82, 0x69, 0x7d, 0xcd, 0x72, 0x9b, 0x80}}
|
||||
FOLDERID_Playlists = &KNOWNFOLDERID{0xde92c1c7, 0x837f, 0x4f69, [8]byte{0xa3, 0xbb, 0x86, 0xe6, 0x31, 0x20, 0x4a, 0x23}}
|
||||
FOLDERID_SamplePlaylists = &KNOWNFOLDERID{0x15ca69b3, 0x30ee, 0x49c1, [8]byte{0xac, 0xe1, 0x6b, 0x5e, 0xc3, 0x72, 0xaf, 0xb5}}
|
||||
FOLDERID_SampleMusic = &KNOWNFOLDERID{0xb250c668, 0xf57d, 0x4ee1, [8]byte{0xa6, 0x3c, 0x29, 0x0e, 0xe7, 0xd1, 0xaa, 0x1f}}
|
||||
FOLDERID_SamplePictures = &KNOWNFOLDERID{0xc4900540, 0x2379, 0x4c75, [8]byte{0x84, 0x4b, 0x64, 0xe6, 0xfa, 0xf8, 0x71, 0x6b}}
|
||||
FOLDERID_SampleVideos = &KNOWNFOLDERID{0x859ead94, 0x2e85, 0x48ad, [8]byte{0xa7, 0x1a, 0x09, 0x69, 0xcb, 0x56, 0xa6, 0xcd}}
|
||||
FOLDERID_PhotoAlbums = &KNOWNFOLDERID{0x69d2cf90, 0xfc33, 0x4fb7, [8]byte{0x9a, 0x0c, 0xeb, 0xb0, 0xf0, 0xfc, 0xb4, 0x3c}}
|
||||
FOLDERID_Public = &KNOWNFOLDERID{0xdfdf76a2, 0xc82a, 0x4d63, [8]byte{0x90, 0x6a, 0x56, 0x44, 0xac, 0x45, 0x73, 0x85}}
|
||||
FOLDERID_ChangeRemovePrograms = &KNOWNFOLDERID{0xdf7266ac, 0x9274, 0x4867, [8]byte{0x8d, 0x55, 0x3b, 0xd6, 0x61, 0xde, 0x87, 0x2d}}
|
||||
FOLDERID_AppUpdates = &KNOWNFOLDERID{0xa305ce99, 0xf527, 0x492b, [8]byte{0x8b, 0x1a, 0x7e, 0x76, 0xfa, 0x98, 0xd6, 0xe4}}
|
||||
FOLDERID_AddNewPrograms = &KNOWNFOLDERID{0xde61d971, 0x5ebc, 0x4f02, [8]byte{0xa3, 0xa9, 0x6c, 0x82, 0x89, 0x5e, 0x5c, 0x04}}
|
||||
FOLDERID_Downloads = &KNOWNFOLDERID{0x374de290, 0x123f, 0x4565, [8]byte{0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b}}
|
||||
FOLDERID_PublicDownloads = &KNOWNFOLDERID{0x3d644c9b, 0x1fb8, 0x4f30, [8]byte{0x9b, 0x45, 0xf6, 0x70, 0x23, 0x5f, 0x79, 0xc0}}
|
||||
FOLDERID_SavedSearches = &KNOWNFOLDERID{0x7d1d3a04, 0xdebb, 0x4115, [8]byte{0x95, 0xcf, 0x2f, 0x29, 0xda, 0x29, 0x20, 0xda}}
|
||||
FOLDERID_QuickLaunch = &KNOWNFOLDERID{0x52a4f021, 0x7b75, 0x48a9, [8]byte{0x9f, 0x6b, 0x4b, 0x87, 0xa2, 0x10, 0xbc, 0x8f}}
|
||||
FOLDERID_Contacts = &KNOWNFOLDERID{0x56784854, 0xc6cb, 0x462b, [8]byte{0x81, 0x69, 0x88, 0xe3, 0x50, 0xac, 0xb8, 0x82}}
|
||||
FOLDERID_SidebarParts = &KNOWNFOLDERID{0xa75d362e, 0x50fc, 0x4fb7, [8]byte{0xac, 0x2c, 0xa8, 0xbe, 0xaa, 0x31, 0x44, 0x93}}
|
||||
FOLDERID_SidebarDefaultParts = &KNOWNFOLDERID{0x7b396e54, 0x9ec5, 0x4300, [8]byte{0xbe, 0x0a, 0x24, 0x82, 0xeb, 0xae, 0x1a, 0x26}}
|
||||
FOLDERID_PublicGameTasks = &KNOWNFOLDERID{0xdebf2536, 0xe1a8, 0x4c59, [8]byte{0xb6, 0xa2, 0x41, 0x45, 0x86, 0x47, 0x6a, 0xea}}
|
||||
FOLDERID_GameTasks = &KNOWNFOLDERID{0x054fae61, 0x4dd8, 0x4787, [8]byte{0x80, 0xb6, 0x09, 0x02, 0x20, 0xc4, 0xb7, 0x00}}
|
||||
FOLDERID_SavedGames = &KNOWNFOLDERID{0x4c5c32ff, 0xbb9d, 0x43b0, [8]byte{0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4}}
|
||||
FOLDERID_Games = &KNOWNFOLDERID{0xcac52c1a, 0xb53d, 0x4edc, [8]byte{0x92, 0xd7, 0x6b, 0x2e, 0x8a, 0xc1, 0x94, 0x34}}
|
||||
FOLDERID_SEARCH_MAPI = &KNOWNFOLDERID{0x98ec0e18, 0x2098, 0x4d44, [8]byte{0x86, 0x44, 0x66, 0x97, 0x93, 0x15, 0xa2, 0x81}}
|
||||
FOLDERID_SEARCH_CSC = &KNOWNFOLDERID{0xee32e446, 0x31ca, 0x4aba, [8]byte{0x81, 0x4f, 0xa5, 0xeb, 0xd2, 0xfd, 0x6d, 0x5e}}
|
||||
FOLDERID_Links = &KNOWNFOLDERID{0xbfb9d5e0, 0xc6a9, 0x404c, [8]byte{0xb2, 0xb2, 0xae, 0x6d, 0xb6, 0xaf, 0x49, 0x68}}
|
||||
FOLDERID_UsersFiles = &KNOWNFOLDERID{0xf3ce0f7c, 0x4901, 0x4acc, [8]byte{0x86, 0x48, 0xd5, 0xd4, 0x4b, 0x04, 0xef, 0x8f}}
|
||||
FOLDERID_UsersLibraries = &KNOWNFOLDERID{0xa302545d, 0xdeff, 0x464b, [8]byte{0xab, 0xe8, 0x61, 0xc8, 0x64, 0x8d, 0x93, 0x9b}}
|
||||
FOLDERID_SearchHome = &KNOWNFOLDERID{0x190337d1, 0xb8ca, 0x4121, [8]byte{0xa6, 0x39, 0x6d, 0x47, 0x2d, 0x16, 0x97, 0x2a}}
|
||||
FOLDERID_OriginalImages = &KNOWNFOLDERID{0x2c36c0aa, 0x5812, 0x4b87, [8]byte{0xbf, 0xd0, 0x4c, 0xd0, 0xdf, 0xb1, 0x9b, 0x39}}
|
||||
FOLDERID_DocumentsLibrary = &KNOWNFOLDERID{0x7b0db17d, 0x9cd2, 0x4a93, [8]byte{0x97, 0x33, 0x46, 0xcc, 0x89, 0x02, 0x2e, 0x7c}}
|
||||
FOLDERID_MusicLibrary = &KNOWNFOLDERID{0x2112ab0a, 0xc86a, 0x4ffe, [8]byte{0xa3, 0x68, 0x0d, 0xe9, 0x6e, 0x47, 0x01, 0x2e}}
|
||||
FOLDERID_PicturesLibrary = &KNOWNFOLDERID{0xa990ae9f, 0xa03b, 0x4e80, [8]byte{0x94, 0xbc, 0x99, 0x12, 0xd7, 0x50, 0x41, 0x04}}
|
||||
FOLDERID_VideosLibrary = &KNOWNFOLDERID{0x491e922f, 0x5643, 0x4af4, [8]byte{0xa7, 0xeb, 0x4e, 0x7a, 0x13, 0x8d, 0x81, 0x74}}
|
||||
FOLDERID_RecordedTVLibrary = &KNOWNFOLDERID{0x1a6fdba2, 0xf42d, 0x4358, [8]byte{0xa7, 0x98, 0xb7, 0x4d, 0x74, 0x59, 0x26, 0xc5}}
|
||||
FOLDERID_HomeGroup = &KNOWNFOLDERID{0x52528a6b, 0xb9e3, 0x4add, [8]byte{0xb6, 0x0d, 0x58, 0x8c, 0x2d, 0xba, 0x84, 0x2d}}
|
||||
FOLDERID_HomeGroupCurrentUser = &KNOWNFOLDERID{0x9b74b6a3, 0x0dfd, 0x4f11, [8]byte{0x9e, 0x78, 0x5f, 0x78, 0x00, 0xf2, 0xe7, 0x72}}
|
||||
FOLDERID_DeviceMetadataStore = &KNOWNFOLDERID{0x5ce4a5e9, 0xe4eb, 0x479d, [8]byte{0xb8, 0x9f, 0x13, 0x0c, 0x02, 0x88, 0x61, 0x55}}
|
||||
FOLDERID_Libraries = &KNOWNFOLDERID{0x1b3ea5dc, 0xb587, 0x4786, [8]byte{0xb4, 0xef, 0xbd, 0x1d, 0xc3, 0x32, 0xae, 0xae}}
|
||||
FOLDERID_PublicLibraries = &KNOWNFOLDERID{0x48daf80b, 0xe6cf, 0x4f4e, [8]byte{0xb8, 0x00, 0x0e, 0x69, 0xd8, 0x4e, 0xe3, 0x84}}
|
||||
FOLDERID_UserPinned = &KNOWNFOLDERID{0x9e3995ab, 0x1f9c, 0x4f13, [8]byte{0xb8, 0x27, 0x48, 0xb2, 0x4b, 0x6c, 0x71, 0x74}}
|
||||
FOLDERID_ImplicitAppShortcuts = &KNOWNFOLDERID{0xbcb5256f, 0x79f6, 0x4cee, [8]byte{0xb7, 0x25, 0xdc, 0x34, 0xe4, 0x02, 0xfd, 0x46}}
|
||||
FOLDERID_AccountPictures = &KNOWNFOLDERID{0x008ca0b1, 0x55b4, 0x4c56, [8]byte{0xb8, 0xa8, 0x4d, 0xe4, 0xb2, 0x99, 0xd3, 0xbe}}
|
||||
FOLDERID_PublicUserTiles = &KNOWNFOLDERID{0x0482af6c, 0x08f1, 0x4c34, [8]byte{0x8c, 0x90, 0xe1, 0x7e, 0xc9, 0x8b, 0x1e, 0x17}}
|
||||
FOLDERID_AppsFolder = &KNOWNFOLDERID{0x1e87508d, 0x89c2, 0x42f0, [8]byte{0x8a, 0x7e, 0x64, 0x5a, 0x0f, 0x50, 0xca, 0x58}}
|
||||
FOLDERID_StartMenuAllPrograms = &KNOWNFOLDERID{0xf26305ef, 0x6948, 0x40b9, [8]byte{0xb2, 0x55, 0x81, 0x45, 0x3d, 0x09, 0xc7, 0x85}}
|
||||
FOLDERID_CommonStartMenuPlaces = &KNOWNFOLDERID{0xa440879f, 0x87a0, 0x4f7d, [8]byte{0xb7, 0x00, 0x02, 0x07, 0xb9, 0x66, 0x19, 0x4a}}
|
||||
FOLDERID_ApplicationShortcuts = &KNOWNFOLDERID{0xa3918781, 0xe5f2, 0x4890, [8]byte{0xb3, 0xd9, 0xa7, 0xe5, 0x43, 0x32, 0x32, 0x8c}}
|
||||
FOLDERID_RoamingTiles = &KNOWNFOLDERID{0x00bcfc5a, 0xed94, 0x4e48, [8]byte{0x96, 0xa1, 0x3f, 0x62, 0x17, 0xf2, 0x19, 0x90}}
|
||||
FOLDERID_RoamedTileImages = &KNOWNFOLDERID{0xaaa8d5a5, 0xf1d6, 0x4259, [8]byte{0xba, 0xa8, 0x78, 0xe7, 0xef, 0x60, 0x83, 0x5e}}
|
||||
FOLDERID_Screenshots = &KNOWNFOLDERID{0xb7bede81, 0xdf94, 0x4682, [8]byte{0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f}}
|
||||
FOLDERID_CameraRoll = &KNOWNFOLDERID{0xab5fb87b, 0x7ce2, 0x4f83, [8]byte{0x91, 0x5d, 0x55, 0x08, 0x46, 0xc9, 0x53, 0x7b}}
|
||||
FOLDERID_SkyDrive = &KNOWNFOLDERID{0xa52bba46, 0xe9e1, 0x435f, [8]byte{0xb3, 0xd9, 0x28, 0xda, 0xa6, 0x48, 0xc0, 0xf6}}
|
||||
FOLDERID_OneDrive = &KNOWNFOLDERID{0xa52bba46, 0xe9e1, 0x435f, [8]byte{0xb3, 0xd9, 0x28, 0xda, 0xa6, 0x48, 0xc0, 0xf6}}
|
||||
FOLDERID_SkyDriveDocuments = &KNOWNFOLDERID{0x24d89e24, 0x2f19, 0x4534, [8]byte{0x9d, 0xde, 0x6a, 0x66, 0x71, 0xfb, 0xb8, 0xfe}}
|
||||
FOLDERID_SkyDrivePictures = &KNOWNFOLDERID{0x339719b5, 0x8c47, 0x4894, [8]byte{0x94, 0xc2, 0xd8, 0xf7, 0x7a, 0xdd, 0x44, 0xa6}}
|
||||
FOLDERID_SkyDriveMusic = &KNOWNFOLDERID{0xc3f2459e, 0x80d6, 0x45dc, [8]byte{0xbf, 0xef, 0x1f, 0x76, 0x9f, 0x2b, 0xe7, 0x30}}
|
||||
FOLDERID_SkyDriveCameraRoll = &KNOWNFOLDERID{0x767e6811, 0x49cb, 0x4273, [8]byte{0x87, 0xc2, 0x20, 0xf3, 0x55, 0xe1, 0x08, 0x5b}}
|
||||
FOLDERID_SearchHistory = &KNOWNFOLDERID{0x0d4c3db6, 0x03a3, 0x462f, [8]byte{0xa0, 0xe6, 0x08, 0x92, 0x4c, 0x41, 0xb5, 0xd4}}
|
||||
FOLDERID_SearchTemplates = &KNOWNFOLDERID{0x7e636bfe, 0xdfa9, 0x4d5e, [8]byte{0xb4, 0x56, 0xd7, 0xb3, 0x98, 0x51, 0xd8, 0xa9}}
|
||||
FOLDERID_CameraRollLibrary = &KNOWNFOLDERID{0x2b20df75, 0x1eda, 0x4039, [8]byte{0x80, 0x97, 0x38, 0x79, 0x82, 0x27, 0xd5, 0xb7}}
|
||||
FOLDERID_SavedPictures = &KNOWNFOLDERID{0x3b193882, 0xd3ad, 0x4eab, [8]byte{0x96, 0x5a, 0x69, 0x82, 0x9d, 0x1f, 0xb5, 0x9f}}
|
||||
FOLDERID_SavedPicturesLibrary = &KNOWNFOLDERID{0xe25b5812, 0xbe88, 0x4bd9, [8]byte{0x94, 0xb0, 0x29, 0x23, 0x34, 0x77, 0xb6, 0xc3}}
|
||||
FOLDERID_RetailDemo = &KNOWNFOLDERID{0x12d4c69e, 0x24ad, 0x4923, [8]byte{0xbe, 0x19, 0x31, 0x32, 0x1c, 0x43, 0xa7, 0x67}}
|
||||
FOLDERID_Device = &KNOWNFOLDERID{0x1c2ac1dc, 0x4358, 0x4b6c, [8]byte{0x97, 0x33, 0xaf, 0x21, 0x15, 0x65, 0x76, 0xf0}}
|
||||
FOLDERID_DevelopmentFiles = &KNOWNFOLDERID{0xdbe8e08e, 0x3053, 0x4bbc, [8]byte{0xb1, 0x83, 0x2a, 0x7b, 0x2b, 0x19, 0x1e, 0x59}}
|
||||
FOLDERID_Objects3D = &KNOWNFOLDERID{0x31c0dd25, 0x9439, 0x4f12, [8]byte{0xbf, 0x41, 0x7f, 0xf4, 0xed, 0xa3, 0x87, 0x22}}
|
||||
FOLDERID_AppCaptures = &KNOWNFOLDERID{0xedc0fe71, 0x98d8, 0x4f4a, [8]byte{0xb9, 0x20, 0xc8, 0xdc, 0x13, 0x3c, 0xb1, 0x65}}
|
||||
FOLDERID_LocalDocuments = &KNOWNFOLDERID{0xf42ee2d3, 0x909f, 0x4907, [8]byte{0x88, 0x71, 0x4c, 0x22, 0xfc, 0x0b, 0xf7, 0x56}}
|
||||
FOLDERID_LocalPictures = &KNOWNFOLDERID{0x0ddd015d, 0xb06c, 0x45d5, [8]byte{0x8c, 0x4c, 0xf5, 0x97, 0x13, 0x85, 0x46, 0x39}}
|
||||
FOLDERID_LocalVideos = &KNOWNFOLDERID{0x35286a68, 0x3c57, 0x41a1, [8]byte{0xbb, 0xb1, 0x0e, 0xae, 0x73, 0xd7, 0x6c, 0x95}}
|
||||
FOLDERID_LocalMusic = &KNOWNFOLDERID{0xa0c69a99, 0x21c8, 0x4671, [8]byte{0x87, 0x03, 0x79, 0x34, 0x16, 0x2f, 0xcf, 0x1d}}
|
||||
FOLDERID_LocalDownloads = &KNOWNFOLDERID{0x7d83ee9b, 0x2244, 0x4e70, [8]byte{0xb1, 0xf5, 0x53, 0x93, 0x04, 0x2a, 0xf1, 0xe4}}
|
||||
FOLDERID_RecordedCalls = &KNOWNFOLDERID{0x2f8b40c2, 0x83ed, 0x48ee, [8]byte{0xb3, 0x83, 0xa1, 0xf1, 0x57, 0xec, 0x6f, 0x9a}}
|
||||
FOLDERID_AllAppMods = &KNOWNFOLDERID{0x7ad67899, 0x66af, 0x43ba, [8]byte{0x91, 0x56, 0x6a, 0xad, 0x42, 0xe6, 0xc5, 0x96}}
|
||||
FOLDERID_CurrentAppMods = &KNOWNFOLDERID{0x3db40b20, 0x2a30, 0x4dbe, [8]byte{0x91, 0x7e, 0x77, 0x1d, 0xd2, 0x1d, 0xd0, 0x99}}
|
||||
FOLDERID_AppDataDesktop = &KNOWNFOLDERID{0xb2c5e279, 0x7add, 0x439f, [8]byte{0xb2, 0x8c, 0xc4, 0x1f, 0xe1, 0xbb, 0xf6, 0x72}}
|
||||
FOLDERID_AppDataDocuments = &KNOWNFOLDERID{0x7be16610, 0x1f7f, 0x44ac, [8]byte{0xbf, 0xf0, 0x83, 0xe1, 0x5f, 0x2f, 0xfc, 0xa1}}
|
||||
FOLDERID_AppDataFavorites = &KNOWNFOLDERID{0x7cfbefbc, 0xde1f, 0x45aa, [8]byte{0xb8, 0x43, 0xa5, 0x42, 0xac, 0x53, 0x6c, 0xc9}}
|
||||
FOLDERID_AppDataProgramData = &KNOWNFOLDERID{0x559d40a3, 0xa036, 0x40fa, [8]byte{0xaf, 0x61, 0x84, 0xcb, 0x43, 0x0a, 0x4d, 0x34}}
|
||||
)
|
||||
4409
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
4409
vendor/golang.org/x/sys/windows/zsyscall_windows.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
vendor/modules.txt
vendored
Normal file
12
vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# github.com/VictoriaMetrics/metrics v1.40.2
|
||||
## explicit; go 1.18
|
||||
github.com/VictoriaMetrics/metrics
|
||||
# github.com/valyala/fastrand v1.1.0
|
||||
## explicit
|
||||
github.com/valyala/fastrand
|
||||
# github.com/valyala/histogram v1.2.0
|
||||
## explicit; go 1.12
|
||||
github.com/valyala/histogram
|
||||
# golang.org/x/sys v0.15.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/sys/windows
|
||||
Reference in New Issue
Block a user