install
This commit is contained in:
parent
834c461515
commit
c24c2f7bb4
|
@ -9,7 +9,7 @@ RUN cd /tmp && \
|
||||||
FROM golang:1.19.1-bullseye AS builder
|
FROM golang:1.19.1-bullseye AS builder
|
||||||
COPY . /go/src/pprofweb/
|
COPY . /go/src/pprofweb/
|
||||||
WORKDIR /go/src/pprofweb
|
WORKDIR /go/src/pprofweb
|
||||||
RUN go build -o server .
|
RUN go build -o server ./webserver
|
||||||
|
|
||||||
FROM gcr.io/distroless/base-debian11:latest AS run
|
FROM gcr.io/distroless/base-debian11:latest AS run
|
||||||
COPY --from=builder /go/src/pprofweb/server /pprofweb
|
COPY --from=builder /go/src/pprofweb/server /pprofweb
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
var CLI struct {
|
||||||
|
Profile Profile `cmd:"" aliases:"p,prof" help:"run a profile and upload"`
|
||||||
|
Upload Upload `cmd:"" aliases:"u,up" help:"upload a file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpClient = &http.Client{}
|
||||||
|
|
||||||
|
type BaseArgs struct {
|
||||||
|
Remote *url.URL `name:"remote" help:"which remote pprofweb server to use" env:"PPROFWEB_REMOTE_URL" default:"https://pprof.tuxpa.in"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
Mode string `name:"mode" short:"m" enum:"heap,goroutine,threacreate,block,mutex,profile" help:"type of profile to run" default:"heap"`
|
||||||
|
Time int `name:"time" short:"t" help:"duration of profile in seconds"`
|
||||||
|
|
||||||
|
Url *url.URL `arg:"" help:"base url of pprof server, ala http://localhost:6060/debug/pprof"`
|
||||||
|
|
||||||
|
BaseArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Profile) Run(ctx Context) error {
|
||||||
|
req := &http.Request{}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
|
req.Method = "GET"
|
||||||
|
req.URL = c.Url
|
||||||
|
req.URL = req.URL.JoinPath(c.Mode)
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
if c.Time > 0 {
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
fmt.Printf("getting profile from %s...\n", req.URL)
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
res, err := doUpload(ctx, c.BaseArgs, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("see profile at %s/%s/ \n", c.Remote, res)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Upload struct {
|
||||||
|
File string `arg:"" help:"file to upload" type:"path"`
|
||||||
|
BaseArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Upload) Run(ctx Context) error {
|
||||||
|
fl, err := os.Open(c.File)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
res, err := doUpload(ctx, c.BaseArgs, fl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("see profile at %s/%s/ \n", c.Remote, res)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doUpload(ctx Context, c BaseArgs, dat io.Reader) (string, error) {
|
||||||
|
fmt.Printf("uploading profile to %s...\n", c.Remote)
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
part, err := writer.CreateFormFile("file", "pprof.pb.gz")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(part, dat)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", c.Remote.JoinPath("upload").String(), body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("content-type", writer.FormDataContentType())
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
bts, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.ToValidUTF8(string(bts), "<22>"), nil
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gfx.cafe/util/go/generic v0.0.0-20220921131905-10a2bde53f66
|
gfx.cafe/util/go/generic v0.0.0-20220921131905-10a2bde53f66
|
||||||
|
github.com/alecthomas/kong v0.7.1
|
||||||
github.com/go-chi/chi/v5 v5.0.7
|
github.com/go-chi/chi/v5 v5.0.7
|
||||||
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0
|
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0
|
||||||
github.com/rs/xid v1.4.0
|
github.com/rs/xid v1.4.0
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -40,6 +40,10 @@ gfx.cafe/util/go/generic v0.0.0-20220921131905-10a2bde53f66 h1:t6agT2M/Kl5iOnhkO
|
||||||
gfx.cafe/util/go/generic v0.0.0-20220921131905-10a2bde53f66/go.mod h1:0mgIMiX8+M1l4VHMaOqBaydZaztbFgblQAaEDOZpUhQ=
|
gfx.cafe/util/go/generic v0.0.0-20220921131905-10a2bde53f66/go.mod h1:0mgIMiX8+M1l4VHMaOqBaydZaztbFgblQAaEDOZpUhQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0=
|
||||||
|
github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
|
||||||
|
github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
|
||||||
|
github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
@ -118,6 +122,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d h1:uGg2frlt3IcT7kbV6LEp5ONv4vmoO2FW4qSO+my/aoM=
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d h1:uGg2frlt3IcT7kbV6LEp5ONv4vmoO2FW4qSO+my/aoM=
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.tuxpa.in/a/pprofweb/cli"
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := NewCLI()
|
||||||
|
sctx, cn := signal.NotifyContext(context.Background(), os.Kill, syscall.SIGTERM, syscall.SIGILL)
|
||||||
|
defer cn()
|
||||||
|
if err := ctx.Run(cli.Context{Context: sctx}); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCLI() *kong.Context {
|
||||||
|
ctx := kong.Parse(&cli.CLI)
|
||||||
|
return ctx
|
||||||
|
}
|
Loading…
Reference in New Issue