1 Star 0 Fork 0

liujinsuo/tool

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
debug_x.go 10.31 KB
一键复制 编辑 原始数据 按行查看 历史
liujinsuo 提交于 2年前 . DebugX
package tool
import (
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
"github.com/gorilla/mux"
"github.com/spf13/cast"
"html/template"
"net/http"
"os/exec"
"strings"
"time"
)
type DebugX struct {
Host string
UrlPath string
GoCmdPath string
}
func NewDebugX(Host string) *DebugX {
arr := []string{"/usr/local/go/bin/go"}
GoCmdPath := ""
for i := 0; i < len(arr); i++ {
ok, err := PathExists(arr[i])
if err != nil {
} else if ok == true {
GoCmdPath = arr[i]
}
}
return &DebugX{
Host: Host,
UrlPath: "debug-x",
GoCmdPath: GoCmdPath,
}
}
// Register 注册到nginx路由
func (s *DebugX) Register(router *gin.Engine) {
router.SetHTMLTemplate(s.Template())
router.GET("/"+s.UrlPath+"/*name", s.Handle)
}
// Register 注册到nginx路由
func (s *DebugX) RegisterMux(router *mux.Router) {
router.Handle("/"+s.UrlPath+`/{name:\w*}`, http.HandlerFunc(s.HandleMux))
}
// SetUrlPath 修改path
func (s *DebugX) SetUrlPath(p string) *DebugX {
s.UrlPath = strings.Trim(p, "/")
return s
}
// SetGoCmdPath 设置go命令路径
func (s *DebugX) SetGoCmdPath(p string) *DebugX {
s.GoCmdPath = p
return s
}
// HeapSvg 查看存活对象的内存分配,
// 内存的指标可通过heap或者allocs查看,这两个指标采样的数据是一样,
// 但是heap可以用来采样存活对象的内存分配(采样前可运行GC,这样剩下的都是存活对象了)。
// 线越粗说明该调用栈内存分配越多
func (s *DebugX) HeapSvg() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/heap?debug=1")
}
func (s *DebugX) HeapTop() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/heap?debug=1")
}
func (s *DebugX) Heap() (cmd string, stdout, stderr []byte, err error) {
res, err := resty.New().R().Get(s.Host + "/debug/pprof/heap?debug=1")
if err != nil {
return
}
stdout = res.Body()
return
}
// ProfileSvg 分析程序热点,比如发现程序中的死循环。
// 开启之后,会定时记录当然程序执行点(就是程序正在执行哪一行代码)以及调用栈,采集一段时间之后(默认30秒),
// 根据这些数据是不是就能分析出来热点代码
// 线越粗说明该调用cpu耗时越多
func (s *DebugX) ProfileSvg(d time.Duration) (cmd string, stdout, stderr []byte, err error) {
seconds := fmt.Sprintf("%v", int(d/time.Second))
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/profile?debug=1&seconds="+seconds)
}
func (s *DebugX) ProfileTop(d time.Duration) (cmd string, stdout, stderr []byte, err error) {
seconds := fmt.Sprintf("%v", int(d/time.Second))
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/profile?debug=1&seconds="+seconds)
}
func (s *DebugX) Profile(d time.Duration) (cmd string, stdout, stderr []byte, err error) {
seconds := fmt.Sprintf("%v", int(d/time.Second))
res, err := resty.New().R().Get(s.Host + "/debug/pprof/profile?debug=1&seconds=" + seconds)
if err != nil {
return
}
stdout = res.Body()
return
}
// AllocsSvg 所有内存分配的采样
func (s *DebugX) AllocsSvg() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/allocs?debug=1")
}
// AllocsTop 所有内存分配的采样
func (s *DebugX) AllocsTop() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/allocs?debug=1")
}
// AllocsTop 所有内存分配的采样
func (s *DebugX) Allocs() (cmd string, stdout, stderr []byte, err error) {
res, err := resty.New().R().Get(s.Host + "/debug/pprof/allocs?debug=1")
if err != nil {
return
}
stdout = res.Body()
return
}
// GoroutineSvg 所有当前 goroutines 的堆栈跟踪
func (s *DebugX) GoroutineSvg() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/goroutine?debug=1")
}
func (s *DebugX) GoroutineTop() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/goroutine?debug=1")
}
func (s *DebugX) Goroutine() (cmd string, stdout, stderr []byte, err error) {
res, err := resty.New().R().Get(s.Host + "/debug/pprof/goroutine?debug=1")
if err != nil {
return
}
stdout = res.Body()
return
}
// BlockSvg block含义是阻塞事件,协程抢锁或者管道的读写阻塞等,都会导致协程的阻塞,
// 协程阻塞时会记录阻塞时间点cputick,解除阻塞时同样记录时间点cputick,以此计算出协程阻塞时间,同时在解除阻塞时记录调用栈。
// mutex含义是锁持有事件,在释放锁时记录时间以及调用栈。
func (s *DebugX) BlockSvg() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/block?debug=1")
}
func (s *DebugX) BlockTop() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/block?debug=1")
}
// MutexSvg 竞争互斥体持有者的堆栈跟踪
// 个人理解 Block应该是包含mutex的
func (s *DebugX) MutexSvg() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-svg", s.Host+"/debug/pprof/mutex?debug=1")
}
// MutexTop 竞争互斥体持有者的堆栈跟踪
func (s *DebugX) MutexTop() (cmd string, stdout, stderr []byte, err error) {
return s.Command(s.GoCmdPath, "tool", "pprof", "-top", s.Host+"/debug/pprof/mutex?debug=1")
}
func (s *DebugX) Command(name string, arg ...string) (cmd string, stdout, stderr []byte, err error) {
c := exec.Command(name, arg...)
cmd = c.String()
Stdout := bytes.Buffer{}
Stderr := bytes.Buffer{}
c.Stdout = &Stdout
c.Stderr = &Stderr
err = c.Run()
stdout = Stdout.Bytes()
stderr = Stderr.Bytes()
return
}
// Template html模版
func (s *DebugX) Template() *template.Template {
return template.Must(template.New("index.html").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
<ul>
<li><a target="blank" href="{{.path}}goroutine_svg">goroutine_svg</a></li>
<li><a target="blank" href="{{.path}}goroutine_top">goroutine_top</a></li>
<li><a target="blank" href="{{.path}}goroutine">goroutine</a></li>
<li><a target="blank" href="{{.path}}profile_svg?seconds=10">profile_svg CPU程序热点</a></li>
<li><a target="blank" href="{{.path}}profile_top?seconds=10">profile_top CPU程序热点</a></li>
<li><a target="blank" href="{{.path}}profile?seconds=10">profile</a></li>
<li><a target="blank" href="{{.path}}allocs_svg">allocs_svg 全部内存</a></li>
<li><a target="blank" href="{{.path}}allocs_top">allocs_top 全部内存</a></li>
<li><a target="blank" href="{{.path}}allocs">allocs</a></li>
<li><a target="blank" href="{{.path}}heap_svg">heap_svg 活跃内存</a></li>
<li><a target="blank" href="{{.path}}heap_top">heap_top 活跃内存</a></li>
<li><a target="blank" href="{{.path}}heap">heap 活跃内存</a></li>
<li><a target="blank" href="{{.path}}block_svg">block_svg 阻塞事件</a></li>
<li><a target="blank" href="{{.path}}block_top">block_top 阻塞事件</a></li>
<li><a target="blank" href="{{.path}}mutex_svg">mutex_svg 互斥锁</a></li>
<li><a target="blank" href="{{.path}}mutex_top">mutex_top 互斥锁</a></li>
</ul>
</body>
</html>`))
}
func (s *DebugX) Handle(ctx *gin.Context) {
name := ctx.Param("name")
s.handleMux(ctx.Writer, ctx.Request, name)
}
func (s *DebugX) HandleMux(writer http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
name := "/" + vars["name"]
s.handleMux(writer, request, name)
}
func (s *DebugX) handleMux(writer http.ResponseWriter, request *http.Request, name string) {
DebugX := s
switch name {
case "", "/", "/index":
if err := s.Template().Execute(writer, map[string]any{
//"path": fmt.Sprintf("/%s/", strings.Trim(ctx.Request.RequestURI, "/")),
"title": "debug-x " + request.Host,
}); err != nil {
writer.WriteHeader(http.StatusInternalServerError)
writer.Write([]byte(err.Error()))
}
return
}
//获取采样时长 秒
secondsStr := request.URL.Query().Get("seconds")
seconds := cast.ToInt(secondsStr)
if seconds <= 0 {
seconds = 1
}
var cmd string
var stdout []byte
var stderr []byte
var err error
var ContentType = "text/html"
switch name {
case "/heap_svg":
cmd, stdout, stderr, err = DebugX.HeapSvg()
case "/heap_top":
cmd, stdout, stderr, err = DebugX.HeapTop()
ContentType = "text/plain"
case "/heap":
cmd, stdout, stderr, err = DebugX.Heap()
ContentType = "text/plain"
case "/allocs_svg":
cmd, stdout, stderr, err = DebugX.AllocsSvg()
case "/allocs_top":
cmd, stdout, stderr, err = DebugX.AllocsTop()
ContentType = "text/plain"
case "/allocs":
cmd, stdout, stderr, err = DebugX.Allocs()
ContentType = "text/plain"
case "/goroutine_svg":
cmd, stdout, stderr, err = DebugX.GoroutineSvg()
case "/goroutine_top":
cmd, stdout, stderr, err = DebugX.GoroutineTop()
ContentType = "text/plain"
case "/goroutine":
cmd, stdout, stderr, err = DebugX.Goroutine()
ContentType = "text/plain"
case "/profile_svg":
cmd, stdout, stderr, err = DebugX.ProfileSvg(time.Second * time.Duration(seconds))
case "/profile_top":
cmd, stdout, stderr, err = DebugX.ProfileTop(time.Second * time.Duration(seconds))
ContentType = "text/plain"
case "/profile":
cmd, stdout, stderr, err = DebugX.Profile(time.Second * time.Duration(seconds))
ContentType = "text/plain"
case "/block_svg":
cmd, stdout, stderr, err = DebugX.BlockSvg()
case "/block_top":
cmd, stdout, stderr, err = DebugX.BlockTop()
ContentType = "text/plain"
case "/mutex_svg":
cmd, stdout, stderr, err = DebugX.MutexSvg()
case "/mutex_top":
cmd, stdout, stderr, err = DebugX.MutexTop()
ContentType = "text/plain"
default:
writer.WriteHeader(http.StatusNotFound)
writer.Write([]byte(fmt.Sprintf("不存在的地址 %+v", request.URL.RequestURI())))
return
}
if err != nil {
writer.WriteHeader(http.StatusOK)
writer.Write([]byte(fmt.Sprintf("命令执行失败 %+v %+v %+v %+v", cmd, string(stdout), string(stderr), err)))
} else {
writer.WriteHeader(http.StatusOK)
writer.Header().Set("Content-Type", ContentType+"; charset=utf-8")
writer.Write(stdout)
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/liujinsuo/tool.git
git@gitee.com:liujinsuo/tool.git
liujinsuo
tool
tool
537076f1d965

搜索帮助