diff --git a/go.mod b/go.mod index 7da5aa29..4f91c0dc 100644 --- a/go.mod +++ b/go.mod @@ -56,9 +56,11 @@ require ( github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/fatih/color v1.15.0 // indirect + github.com/felixge/fgprof v0.9.5 // indirect github.com/gaissmai/bart v0.9.5 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect diff --git a/go.sum b/go.sum index 60956b55..cd3c004c 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,12 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99k github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -65,6 +71,8 @@ github.com/ebitengine/purego v0.4.0 h1:RQVuMIxQPQ5iCGEJvjQ17YOK+1tMKjVau2FUMvXH4 github.com/ebitengine/purego v0.4.0/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= +github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -74,6 +82,9 @@ github.com/gaissmai/bart v0.9.5 h1:vy+r4Px6bjZ+v2QYXAsg63vpz9IfzdW146A8Cn4GPIo= github.com/gaissmai/bart v0.9.5/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -100,6 +111,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -111,6 +124,8 @@ github.com/hdm/jarm-go v0.0.7/go.mod h1:kinGoS0+Sdn1Rr54OtanET5E5n7AlD6T6CrJAKDj github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= @@ -131,12 +146,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -178,6 +195,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -370,6 +388,7 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pprof/README.md b/pprof/README.md index 783597eb..53aca0d6 100644 --- a/pprof/README.md +++ b/pprof/README.md @@ -1,6 +1,13 @@ ## PProfiling Usage Guide -### Environment Variables +Two types of profiling are supported: + +1. **pprof**: Standard go profiling writing to files in a directory. +2. **server**: Profiling server listening on a port with pprof and fgprof endpoints. + +### pprof + +#### Environment Variables - `PPROF`: Enable or disable profiling. Set to 1 to enable. - `MEM_PROFILE_DIR`: Directory to write memory profiles to. @@ -9,7 +16,7 @@ - `MEM_PROFILE_RATE`: Memory profiling rate (default 4096). -## How to Use +### How to Use 1. Set the environment variables as per your requirements. @@ -30,7 +37,7 @@ export MEM_PROFILE_RATE=4096 - Profiles will be written at intervals specified by PPROF_TIME. - Memory profiling rate is controlled by MEM_PROFILE_RATE. -### Example +#### Example ```bash [+] GOOS: linux @@ -46,7 +53,31 @@ profile: ticker enabled (rate 10s) profile: cpu profiling enabled (ticker 10s) ``` -### Note +#### Note - The polling time (PPROF_TIME) should be set according to your application's performance and profiling needs. -- The memory profiling rate (MEM_PROFILE_RATE) controls the granularity of the memory profiling. Higher values provide more detail but consume more resources. \ No newline at end of file +- The memory profiling rate (MEM_PROFILE_RATE) controls the granularity of the memory profiling. Higher values provide more detail but consume more resources. + +### server + +Server is a simple HTTP server listening on a port with pprof and fgprof endpoints. + +#### Environment Variables + +- `PPROF_SERVER_ADDR`: Address to listen on for pprof and fgprof server (default 127.0.0.1:6060). + +#### Endpoints + +- /debug/pprof/ +- /debug/pprof/cmdline +- /debug/pprof/profile +- /debug/pprof/profile +- /debug/pprof/symbol +- /debug/pprof/trace +- /debug/fgprof + +#### Example + +```console +go tool pprof http://127.0.0.1:8086/debug/fgprof +``` \ No newline at end of file diff --git a/pprof/pprof.go b/pprof/pprof.go index 653b9ad3..62d26ef3 100644 --- a/pprof/pprof.go +++ b/pprof/pprof.go @@ -24,65 +24,69 @@ const ( func init() { if env.GetEnvOrDefault(PPROFSwitchENV, 0) == 1 { - log.Printf("[+] GOOS: %v\n", runtime.GOOS) - log.Printf("[+] GOARCH: %v\n", runtime.GOARCH) - log.Printf("[+] Command: %v\n", strings.Join(os.Args, " ")) - log.Println("Available PPROF Config Options:") - log.Printf("%-16v - directory to write memory profiles to\n", MemProfileENV) - log.Printf("%-16v - directory to write cpu profiles to\n", CPUProfileENV) - log.Printf("%-16v - polling time for cpu and memory profiles (with unit ex: 10s)\n", PPROFTimeENV) - log.Printf("%-16v - memory profiling rate (default 4096)\n", MemProfileRate) + startDefaultProfiler() + } +} + +func startDefaultProfiler() { + log.Printf("[+] GOOS: %v\n", runtime.GOOS) + log.Printf("[+] GOARCH: %v\n", runtime.GOARCH) + log.Printf("[+] Command: %v\n", strings.Join(os.Args, " ")) + log.Println("Available PPROF Config Options:") + log.Printf("%-16v - directory to write memory profiles to\n", MemProfileENV) + log.Printf("%-16v - directory to write cpu profiles to\n", CPUProfileENV) + log.Printf("%-16v - polling time for cpu and memory profiles (with unit ex: 10s)\n", PPROFTimeENV) + log.Printf("%-16v - memory profiling rate (default 4096)\n", MemProfileRate) - memProfilesDir := env.GetEnvOrDefault(MemProfileENV, "memdump") - cpuProfilesDir := env.GetEnvOrDefault(CPUProfileENV, "cpuprofile") - pprofTimeDuration := env.GetEnvOrDefault(PPROFTimeENV, time.Duration(3)*time.Second) - pprofRate := env.GetEnvOrDefault(MemProfileRate, 4096) + memProfilesDir := env.GetEnvOrDefault(MemProfileENV, "memdump") + cpuProfilesDir := env.GetEnvOrDefault(CPUProfileENV, "cpuprofile") + pprofTimeDuration := env.GetEnvOrDefault(PPROFTimeENV, time.Duration(3)*time.Second) + pprofRate := env.GetEnvOrDefault(MemProfileRate, 4096) - _ = os.MkdirAll(memProfilesDir, 0755) - _ = os.MkdirAll(cpuProfilesDir, 0755) + _ = os.MkdirAll(memProfilesDir, 0755) + _ = os.MkdirAll(cpuProfilesDir, 0755) - runtime.MemProfileRate = pprofRate - log.Printf("profile: memory profiling enabled (rate %d), %s\n", runtime.MemProfileRate, memProfilesDir) - log.Printf("profile: ticker enabled (rate %s)\n", pprofTimeDuration) + runtime.MemProfileRate = pprofRate + log.Printf("profile: memory profiling enabled (rate %d), %s\n", runtime.MemProfileRate, memProfilesDir) + log.Printf("profile: ticker enabled (rate %s)\n", pprofTimeDuration) - // cpu ticker and profiler - go func() { - ticker := time.NewTicker(pprofTimeDuration) - count := 0 - buff := bytes.Buffer{} - log.Printf("profile: cpu profiling enabled (ticker %s)\n", pprofTimeDuration) - for { - err := pprof.StartCPUProfile(&buff) - if err != nil { - log.Fatalf("profile: could not start cpu profile: %s\n", err) - } - <-ticker.C - pprof.StopCPUProfile() - if err := os.WriteFile(filepath.Join(cpuProfilesDir, "cpuprofile-t"+strconv.Itoa(count)+".out"), buff.Bytes(), 0755); err != nil { - log.Fatalf("profile: could not write cpu profile: %s\n", err) - } - buff.Reset() - count++ + // cpu ticker and profiler + go func() { + ticker := time.NewTicker(pprofTimeDuration) + count := 0 + buff := bytes.Buffer{} + log.Printf("profile: cpu profiling enabled (ticker %s)\n", pprofTimeDuration) + for { + err := pprof.StartCPUProfile(&buff) + if err != nil { + log.Fatalf("profile: could not start cpu profile: %s\n", err) } - }() + <-ticker.C + pprof.StopCPUProfile() + if err := os.WriteFile(filepath.Join(cpuProfilesDir, "cpuprofile-t"+strconv.Itoa(count)+".out"), buff.Bytes(), 0755); err != nil { + log.Fatalf("profile: could not write cpu profile: %s\n", err) + } + buff.Reset() + count++ + } + }() - // memory ticker and profiler - go func() { - ticker := time.NewTicker(pprofTimeDuration) - count := 0 - log.Printf("profile: memory profiling enabled (ticker %s)\n", pprofTimeDuration) - for { - <-ticker.C - var buff bytes.Buffer - if err := pprof.WriteHeapProfile(&buff); err != nil { - log.Printf("profile: could not write memory profile: %s\n", err) - } - err := os.WriteFile(filepath.ToSlash(filepath.Join(memProfilesDir, "memprofile-t"+strconv.Itoa(count)+".out")), buff.Bytes(), 0755) - if err != nil { - log.Printf("profile: could not write memory profile: %s\n", err) - } - count++ + // memory ticker and profiler + go func() { + ticker := time.NewTicker(pprofTimeDuration) + count := 0 + log.Printf("profile: memory profiling enabled (ticker %s)\n", pprofTimeDuration) + for { + <-ticker.C + var buff bytes.Buffer + if err := pprof.WriteHeapProfile(&buff); err != nil { + log.Printf("profile: could not write memory profile: %s\n", err) } - }() - } + err := os.WriteFile(filepath.ToSlash(filepath.Join(memProfilesDir, "memprofile-t"+strconv.Itoa(count)+".out")), buff.Bytes(), 0755) + if err != nil { + log.Printf("profile: could not write memory profile: %s\n", err) + } + count++ + } + }() } diff --git a/pprof/server.go b/pprof/server.go new file mode 100644 index 00000000..028372f5 --- /dev/null +++ b/pprof/server.go @@ -0,0 +1,62 @@ +package pprof + +import ( + "context" + "net/http" + "net/http/pprof" + "runtime" + "time" + + "github.com/felixge/fgprof" + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/utils/env" +) + +const ( + PPROFServerAddressENV = "PPROF_SERVER_ADDRESS" +) + +type PprofServer struct { + server *http.Server +} + +func NewPprofServer() *PprofServer { + address := env.GetEnvOrDefault(PPROFServerAddressENV, "127.0.0.1:8086") + + mux := http.NewServeMux() + // Default pprof handlers + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + + // Also add fgprof for more detailed profiling + mux.Handle("/debug/fgprof", fgprof.Handler()) + + server := &http.Server{ + Addr: address, + Handler: mux, + } + // Enable block and mutex profiling as well + runtime.SetBlockProfileRate(1) + runtime.SetMutexProfileFraction(1) + + return &PprofServer{server: server} +} + +func (p *PprofServer) Start() { + gologger.Info().Msgf("Listening pprof debug server on: %s", p.server.Addr) + + go func() { + if err := p.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + gologger.Error().Msgf("pprof server failed to start: %s", err) + } + }() +} + +func (p *PprofServer) Stop() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + _ = p.server.Shutdown(ctx) +}