mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
collect cpu and trace profiles with the promtool debug command (#4897)
Signed-off-by: Krasi Georgiev <kgeorgie@redhat.com>
This commit is contained in:
parent
f25a6baedb
commit
080e6ed31a
|
@ -14,112 +14,56 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/google/pprof/profile"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type debugWriterConfig struct {
|
type debugWriterConfig struct {
|
||||||
serverURL string
|
serverURL string
|
||||||
tarballName string
|
tarballName string
|
||||||
pathToFileName map[string]string
|
endPointGroups []endpointsGroup
|
||||||
postProcess func(b []byte) ([]byte, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type debugWriter struct {
|
func debugWrite(cfg debugWriterConfig) error {
|
||||||
archiver
|
|
||||||
httpClient
|
|
||||||
requestToFile map[*http.Request]string
|
|
||||||
postProcess func(b []byte) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDebugWriter(cfg debugWriterConfig) (*debugWriter, error) {
|
|
||||||
client, err := newPrometheusHTTPClient(cfg.serverURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
archiver, err := newTarGzFileWriter(cfg.tarballName)
|
archiver, err := newTarGzFileWriter(cfg.tarballName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return errors.Wrap(err, "error creating a new archiver")
|
||||||
}
|
}
|
||||||
reqs := make(map[*http.Request]string)
|
|
||||||
for path, filename := range cfg.pathToFileName {
|
|
||||||
req, err := http.NewRequest(http.MethodGet, client.urlJoin(path), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
reqs[req] = filename
|
|
||||||
}
|
|
||||||
return &debugWriter{
|
|
||||||
archiver,
|
|
||||||
client,
|
|
||||||
reqs,
|
|
||||||
cfg.postProcess,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *debugWriter) Write() int {
|
for _, endPointGroup := range cfg.endPointGroups {
|
||||||
for req, filename := range w.requestToFile {
|
for url, filename := range endPointGroup.urlToFilename {
|
||||||
_, body, err := w.do(req)
|
url := cfg.serverURL + url
|
||||||
if err != nil {
|
fmt.Println("collecting:", url)
|
||||||
fmt.Fprintln(os.Stderr, "error executing HTTP request:", err)
|
res, err := http.Get(url)
|
||||||
return 1
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error executing HTTP request")
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
res.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error reading the response body")
|
||||||
|
}
|
||||||
|
|
||||||
|
if endPointGroup.postProcess != nil {
|
||||||
|
body, err = endPointGroup.postProcess(body)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error post-processing HTTP response body")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := archiver.write(filename, body); err != nil {
|
||||||
|
return errors.Wrap(err, "error writing into the archive")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := w.postProcess(body)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "error post-processing HTTP response body:", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.archiver.write(filename, buf); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "error writing into archive:", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.close(); err != nil {
|
if err := archiver.close(); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "error closing archiver:", err)
|
return errors.Wrap(err, "error closing archive writer")
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Compiling debug information complete, all files written in %q.\n", w.filename())
|
fmt.Printf("Compiling debug information complete, all files written in %q.\n", cfg.tarballName)
|
||||||
return 0
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func validate(b []byte) (*profile.Profile, error) {
|
|
||||||
p, err := profile.Parse(bytes.NewReader(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var pprofPostProcess = func(b []byte) ([]byte, error) {
|
|
||||||
p, err := validate(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := p.WriteUncompressed(&buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fmt.Println(p.String())
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var metricsPostProcess = func(b []byte) ([]byte, error) {
|
|
||||||
fmt.Println(string(b))
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var allPostProcess = func(b []byte) ([]byte, error) {
|
|
||||||
_, err := validate(b)
|
|
||||||
if err != nil {
|
|
||||||
return metricsPostProcess(b)
|
|
||||||
}
|
|
||||||
return pprofPostProcess(b)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -27,6 +28,8 @@ import (
|
||||||
|
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
|
|
||||||
|
"github.com/google/pprof/profile"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/prometheus/client_golang/api"
|
"github.com/prometheus/client_golang/api"
|
||||||
"github.com/prometheus/client_golang/api/prometheus/v1"
|
"github.com/prometheus/client_golang/api/prometheus/v1"
|
||||||
config_util "github.com/prometheus/common/config"
|
config_util "github.com/prometheus/common/config"
|
||||||
|
@ -510,61 +513,85 @@ func parseTime(s string) (time.Time, error) {
|
||||||
return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
|
return time.Time{}, fmt.Errorf("cannot parse %q to a valid timestamp", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugPprof(url string) int {
|
type endpointsGroup struct {
|
||||||
w, err := newDebugWriter(debugWriterConfig{
|
urlToFilename map[string]string
|
||||||
serverURL: url,
|
postProcess func(b []byte) ([]byte, error)
|
||||||
tarballName: "debug.tar.gz",
|
}
|
||||||
pathToFileName: map[string]string{
|
|
||||||
"/debug/pprof/block": "block.pb",
|
var (
|
||||||
"/debug/pprof/goroutine": "goroutine.pb",
|
pprofEndpoints = []endpointsGroup{
|
||||||
"/debug/pprof/heap": "heap.pb",
|
{
|
||||||
"/debug/pprof/mutex": "mutex.pb",
|
urlToFilename: map[string]string{
|
||||||
"/debug/pprof/threadcreate": "threadcreate.pb",
|
"/debug/pprof/profile?seconds=30": "cpu.pb",
|
||||||
|
"/debug/pprof/block": "block.pb",
|
||||||
|
"/debug/pprof/goroutine": "goroutine.pb",
|
||||||
|
"/debug/pprof/heap": "heap.pb",
|
||||||
|
"/debug/pprof/mutex": "mutex.pb",
|
||||||
|
"/debug/pprof/threadcreate": "threadcreate.pb",
|
||||||
|
},
|
||||||
|
postProcess: func(b []byte) ([]byte, error) {
|
||||||
|
p, err := profile.Parse(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := p.WriteUncompressed(&buf); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "writing the profile to the buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
postProcess: pprofPostProcess,
|
{
|
||||||
})
|
urlToFilename: map[string]string{
|
||||||
if err != nil {
|
"/debug/pprof/trace?seconds=30": "trace.pb",
|
||||||
fmt.Fprintln(os.Stderr, "error creating debug writer:", err)
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
metricsEndpoints = []endpointsGroup{
|
||||||
|
{
|
||||||
|
urlToFilename: map[string]string{
|
||||||
|
"/metrics": "metrics.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
allEndpoints = append(pprofEndpoints, metricsEndpoints...)
|
||||||
|
)
|
||||||
|
|
||||||
|
func debugPprof(url string) int {
|
||||||
|
if err := debugWrite(debugWriterConfig{
|
||||||
|
serverURL: url,
|
||||||
|
tarballName: "debug.tar.gz",
|
||||||
|
endPointGroups: pprofEndpoints,
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "error completing debug command:", err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return w.Write()
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugMetrics(url string) int {
|
func debugMetrics(url string) int {
|
||||||
w, err := newDebugWriter(debugWriterConfig{
|
if err := debugWrite(debugWriterConfig{
|
||||||
serverURL: url,
|
serverURL: url,
|
||||||
tarballName: "debug.tar.gz",
|
tarballName: "debug.tar.gz",
|
||||||
pathToFileName: map[string]string{
|
endPointGroups: metricsEndpoints,
|
||||||
"/metrics": "metrics.txt",
|
}); err != nil {
|
||||||
},
|
fmt.Fprintln(os.Stderr, "error completing debug command:", err)
|
||||||
postProcess: metricsPostProcess,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "error creating debug writer:", err)
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return w.Write()
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugAll(url string) int {
|
func debugAll(url string) int {
|
||||||
w, err := newDebugWriter(debugWriterConfig{
|
if err := debugWrite(debugWriterConfig{
|
||||||
serverURL: url,
|
serverURL: url,
|
||||||
tarballName: "debug.tar.gz",
|
tarballName: "debug.tar.gz",
|
||||||
pathToFileName: map[string]string{
|
endPointGroups: allEndpoints,
|
||||||
"/debug/pprof/block": "block.pb",
|
}); err != nil {
|
||||||
"/debug/pprof/goroutine": "goroutine.pb",
|
fmt.Fprintln(os.Stderr, "error completing debug command:", err)
|
||||||
"/debug/pprof/heap": "heap.pb",
|
|
||||||
"/debug/pprof/mutex": "mutex.pb",
|
|
||||||
"/debug/pprof/threadcreate": "threadcreate.pb",
|
|
||||||
"/metrics": "metrics.txt",
|
|
||||||
},
|
|
||||||
postProcess: allPostProcess,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "error creating debug writer:", err)
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
return w.Write()
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type printer interface {
|
type printer interface {
|
||||||
|
@ -583,7 +610,7 @@ func (p *promqlPrinter) printSeries(val []model.LabelSet) {
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (j *promqlPrinter) printLabelValues(val model.LabelValues) {
|
func (p *promqlPrinter) printLabelValues(val model.LabelValues) {
|
||||||
for _, v := range val {
|
for _, v := range val {
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue