Compare commits
2 Commits
6b94209284
...
cd7f02a2c0
Author | SHA1 | Date |
---|---|---|
a | cd7f02a2c0 | |
a | c24c2f7bb4 |
|
@ -9,7 +9,7 @@ RUN cd /tmp && \
|
|||
FROM golang:1.19.1-bullseye AS builder
|
||||
COPY . /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
|
||||
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 (
|
||||
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/google/pprof v0.0.0-20211108044417-e9b028704de0
|
||||
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=
|
||||
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/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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
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/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/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-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
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