Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: use pprof profile to collect CPU time group by SQL and plan digest #24892

Merged
merged 67 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
d9f72f2
init
crazycs520 Apr 28, 2021
a3e1207
refine output
crazycs520 Apr 28, 2021
37af9ee
add config and rename pkg name
crazycs520 May 19, 2021
ebbbf9e
refine sleep interval
crazycs520 May 19, 2021
4d6d2ff
add plan digest
crazycs520 May 19, 2021
8754beb
fix bug for execute prepare statements
crazycs520 May 19, 2021
469d67c
fix exec stmt plan digest bug
crazycs520 May 20, 2021
037f854
add debug config to output profile
crazycs520 May 20, 2021
4628d01
fix pprof profile http api
crazycs520 May 20, 2021
13fd80f
dev
crazycs520 May 24, 2021
64d39b4
add collector interface
crazycs520 May 24, 2021
251e725
fix bug of execute prepare stmt doesn't collected plan
crazycs520 May 25, 2021
fe2367f
Merge branch 'master' of https://github.com/pingcap/tidb into stmt-cp…
crazycs520 May 25, 2021
7814dcb
refine code
crazycs520 May 25, 2021
f6b2c20
refine code
crazycs520 May 25, 2021
ae0997c
refine code
crazycs520 May 25, 2021
12038b3
add test and refine code
crazycs520 May 25, 2021
49d099b
refine test and add license
crazycs520 May 25, 2021
e74b1f3
refine code and comment
crazycs520 May 25, 2021
841139a
Merge branch 'master' into stmt-cpu-dev
crazycs520 May 25, 2021
469f6eb
fix race test
crazycs520 May 26, 2021
e987890
Merge branch 'stmt-cpu-dev' of https://github.com/crazycs520/tidb int…
crazycs520 May 26, 2021
db33a40
make test stable
crazycs520 May 26, 2021
9444817
rename variable
crazycs520 May 26, 2021
9a54407
add sleep if profile failed
crazycs520 May 26, 2021
7340de5
Merge branch 'master' into stmt-cpu-dev
crazycs520 May 26, 2021
2026c29
reduce normalize
crazycs520 May 26, 2021
fa98a85
add more test
crazycs520 May 26, 2021
04d7838
add more comment
crazycs520 May 27, 2021
15986e2
add link
crazycs520 May 27, 2021
e4610cd
Merge branch 'master' of https://github.com/pingcap/tidb into stmt-cp…
crazycs520 May 27, 2021
080dba1
use prepared stmt normalized sql to avoid re-normalize the prepare sql
crazycs520 May 27, 2021
3e43ffa
address comment
crazycs520 May 27, 2021
684aa3a
address comment and add fix me comment
crazycs520 May 27, 2021
65fd410
change top sql enable config default value to false
crazycs520 May 27, 2021
2723609
fix tiny test bug and address comment
crazycs520 May 28, 2021
979a7cf
Merge branch 'master' into stmt-cpu-dev
crazycs520 May 28, 2021
7c81f98
rename function name to make code clear
crazycs520 May 28, 2021
eead580
address comment
crazycs520 May 28, 2021
8d33e88
address comment
crazycs520 May 28, 2021
d807479
rename and output optimize time cost
crazycs520 May 31, 2021
a4e15ff
use atomic to avoid race
crazycs520 May 31, 2021
3193c64
address comment
crazycs520 May 31, 2021
b877e05
Merge branch 'master' into stmt-cpu-dev
crazycs520 May 31, 2021
51f4127
address comment
crazycs520 May 31, 2021
eb59909
address comment
crazycs520 May 31, 2021
e606073
address comment
crazycs520 May 31, 2021
26786da
avoid double digest cost for top sql
crazycs520 May 31, 2021
d473ef3
address comment
crazycs520 May 31, 2021
5c49299
Merge branch 'master' of https://github.com/pingcap/tidb into stmt-cp…
crazycs520 May 31, 2021
cfcf574
add topsql pkg
crazycs520 May 31, 2021
889ac43
Merge branch 'master' into stmt-cpu-dev
crazycs520 May 31, 2021
fdad7e9
refine comment
crazycs520 May 31, 2021
2e648bc
refine code and address comment
crazycs520 May 31, 2021
d731285
refactor the top sql api
crazycs520 May 31, 2021
fca381d
tiny refactor collector and address comment
crazycs520 May 31, 2021
e8a153f
Merge branch 'master' into stmt-cpu-dev
crazycs520 Jun 1, 2021
d737aa0
use []byte instead of string as digest for top sql
crazycs520 Jun 1, 2021
657be65
Update util/topsql/topsql.go
crazycs520 Jun 1, 2021
18ca022
address comment
crazycs520 Jun 1, 2021
612839c
return ctx
crazycs520 Jun 1, 2021
42c8081
address comment
crazycs520 Jun 1, 2021
a9b00b9
Update executor/executor.go
crazycs520 Jun 1, 2021
890d287
set label as early as posible
crazycs520 Jun 1, 2021
1f4d10b
Merge branch 'master' into stmt-cpu-dev
crazycs520 Jun 1, 2021
027ef61
Merge branch 'master' into stmt-cpu-dev
crazycs520 Jun 2, 2021
75e8d94
Merge branch 'master' into stmt-cpu-dev
crazycs520 Jun 2, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions executor/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/pingcap/errors"
"github.com/pingcap/failpoint"
"github.com/pingcap/log"
"github.com/pingcap/parser"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
Expand Down Expand Up @@ -56,7 +57,7 @@ import (
"github.com/pingcap/tidb/util/sqlexec"
"github.com/pingcap/tidb/util/stmtsummary"
"github.com/pingcap/tidb/util/stringutil"

"github.com/pingcap/tidb/util/topsql"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
Expand Down Expand Up @@ -213,6 +214,7 @@ func (a *ExecStmt) PointGet(ctx context.Context, is infoschema.InfoSchema) (*rec
defer span1.Finish()
ctx = opentracing.ContextWithSpan(ctx, span1)
}
a.setPlanLabelForTopSQL(ctx)
startTs := uint64(math.MaxUint64)
err := a.Ctx.InitTxnWithStartTS(startTs)
if err != nil {
Expand Down Expand Up @@ -288,6 +290,17 @@ func (a *ExecStmt) RebuildPlan(ctx context.Context) (int64, error) {
return a.InfoSchema.SchemaMetaVersion(), nil
}

func (a *ExecStmt) setPlanLabelForTopSQL(ctx context.Context) {
if a.Plan == nil || !variable.TopSQLEnabled() {
return
}
normalizedSQL, sqlDigest := a.Ctx.GetSessionVars().StmtCtx.SQLDigest()
normalizedPlan, planDigest := getPlanDigest(a.Ctx, a.Plan)
if len(normalizedPlan) > 0 {
crazycs520 marked this conversation as resolved.
Show resolved Hide resolved
topsql.AttachSQLInfo(ctx, normalizedSQL, sqlDigest, normalizedPlan, planDigest)
}
crazycs520 marked this conversation as resolved.
Show resolved Hide resolved
}

// Exec builds an Executor from a plan. If the Executor doesn't return result,
// like the INSERT, UPDATE statements, it executes in this function, if the Executor returns
// result, execution is done after this function returns, in the returned sqlexec.RecordSet Next method.
Expand Down Expand Up @@ -357,8 +370,8 @@ func (a *ExecStmt) Exec(ctx context.Context) (_ sqlexec.RecordSet, err error) {
if err != nil {
return nil, err
}

getPlanDigest(a.Ctx, a.Plan)
// ExecuteExec will rewrite `a.Plan`, so set plan label should be executed after `a.buildExecutor`.
a.setPlanLabelForTopSQL(ctx)

if err = e.Open(ctx); err != nil {
terror.Call(e.Close)
Expand Down Expand Up @@ -951,7 +964,7 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) {
statsInfos := plannercore.GetStatsInfo(a.Plan)
memMax := sessVars.StmtCtx.MemTracker.MaxConsumed()
diskMax := sessVars.StmtCtx.DiskTracker.MaxConsumed()
planDigest := getPlanDigest(a.Ctx, a.Plan)
_, planDigest := getPlanDigest(a.Ctx, a.Plan)
slowItems := &variable.SlowQueryLogItems{
TxnTS: txnTS,
SQL: sql.String(),
Expand All @@ -969,7 +982,7 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) {
DiskMax: diskMax,
Succ: succ,
Plan: getPlanTree(a.Ctx, a.Plan),
PlanDigest: planDigest,
PlanDigest: planDigest.String(),
Prepared: a.isPreparedStmt,
HasMoreResults: hasMoreResults,
PlanFromCache: sessVars.FoundInPlanCache,
Expand Down Expand Up @@ -1043,15 +1056,15 @@ func getPlanTree(sctx sessionctx.Context, p plannercore.Plan) string {
}

// getPlanDigest will try to get the select plan tree if the plan is select or the select plan of delete/update/insert statement.
func getPlanDigest(sctx sessionctx.Context, p plannercore.Plan) string {
func getPlanDigest(sctx sessionctx.Context, p plannercore.Plan) (string, *parser.Digest) {
sc := sctx.GetSessionVars().StmtCtx
_, planDigest := sc.GetPlanDigest()
if planDigest != nil {
return planDigest.String()
normalized, planDigest := sc.GetPlanDigest()
if len(normalized) > 0 && planDigest != nil {
return normalized, planDigest
}
normalized, planDigest := plannercore.NormalizePlan(p)
normalized, planDigest = plannercore.NormalizePlan(p)
sc.SetPlanDigest(normalized, planDigest)
return planDigest.String()
return normalized, planDigest
}

// getEncodedPlan gets the encoded plan, and generates the hint string if indicated.
Expand Down Expand Up @@ -1125,11 +1138,12 @@ func (a *ExecStmt) SummaryStmt(succ bool) {
var planDigestGen func() string
if a.Plan.TP() == plancodec.TypePointGet {
planDigestGen = func() string {
planDigest := getPlanDigest(a.Ctx, a.Plan)
return planDigest
_, planDigest := getPlanDigest(a.Ctx, a.Plan)
return planDigest.String()
}
} else {
planDigest = getPlanDigest(a.Ctx, a.Plan)
_, tmp := getPlanDigest(a.Ctx, a.Plan)
planDigest = tmp.String()
}

execDetail := stmtCtx.GetExecDetails()
Expand Down
17 changes: 15 additions & 2 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"math"
"runtime"
"runtime/pprof"
"runtime/trace"
"strconv"
"strings"
Expand Down Expand Up @@ -66,6 +67,7 @@ import (
"github.com/pingcap/tidb/util/logutil"
"github.com/pingcap/tidb/util/memory"
"github.com/pingcap/tidb/util/resourcegrouptag"
"github.com/pingcap/tidb/util/topsql"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -1652,9 +1654,20 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
sc.MemTracker.SetActionOnExceed(action)
}
if execStmt, ok := s.(*ast.ExecuteStmt); ok {
s, err = planner.GetPreparedStmt(execStmt, vars)
prepareStmt, err := planner.GetPreparedStmt(execStmt, vars)
if err != nil {
return
return err
}
s = prepareStmt.PreparedAst.Stmt
sc.InitSQLDigest(prepareStmt.NormalizedSQL, prepareStmt.SQLDigest)
// For `execute stmt` SQL, should reset the SQL digest with the prepare SQL digest.
goCtx := context.Background()
if variable.EnablePProfSQLCPU.Load() && len(prepareStmt.NormalizedSQL) > 0 {
goCtx = pprof.WithLabels(goCtx, pprof.Labels("sql", util.QueryStrForLog(prepareStmt.NormalizedSQL)))
pprof.SetGoroutineLabels(goCtx)
}
if variable.TopSQLEnabled() && prepareStmt.SQLDigest != nil {
topsql.AttachSQLInfo(goCtx, prepareStmt.NormalizedSQL, prepareStmt.SQLDigest, "", nil)
crazycs520 marked this conversation as resolved.
Show resolved Hide resolved
}
}
// execute missed stmtID uses empty sql
Expand Down
9 changes: 7 additions & 2 deletions executor/prepared.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ import (
"github.com/pingcap/tidb/planner"
plannercore "github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/types"
driver "github.com/pingcap/tidb/types/parser_driver"
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/hint"
"github.com/pingcap/tidb/util/sqlexec"
"github.com/pingcap/tidb/util/topsql"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -178,6 +180,10 @@ func (e *PrepareExec) Next(ctx context.Context, req *chunk.Chunk) error {
Params: sorter.markers,
SchemaVersion: ret.InfoSchema.SchemaMetaVersion(),
}
normalizedSQL, digest := parser.NormalizeDigest(prepared.Stmt.Text())
if variable.TopSQLEnabled() {
topsql.AttachSQLInfo(ctx, normalizedSQL, digest, "", nil)
}

if !plannercore.PreparedPlanCacheEnabled() {
prepared.UseCache = false
Expand Down Expand Up @@ -213,11 +219,10 @@ func (e *PrepareExec) Next(ctx context.Context, req *chunk.Chunk) error {
vars.PreparedStmtNameToID[e.name] = e.ID
}

normalized, digest := parser.NormalizeDigest(prepared.Stmt.Text())
preparedObj := &plannercore.CachedPrepareStmt{
PreparedAst: prepared,
VisitInfos: destBuilder.GetVisitInfo(),
NormalizedSQL: normalized,
NormalizedSQL: normalizedSQL,
SQLDigest: digest,
ForUpdateRead: destBuilder.GetIsForUpdateRead(),
}
Expand Down
8 changes: 4 additions & 4 deletions planner/optimize.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import (
)

// GetPreparedStmt extract the prepared statement from the execute statement.
func GetPreparedStmt(stmt *ast.ExecuteStmt, vars *variable.SessionVars) (ast.StmtNode, error) {
func GetPreparedStmt(stmt *ast.ExecuteStmt, vars *variable.SessionVars) (*plannercore.CachedPrepareStmt, error) {
var ok bool
execID := stmt.ExecID
if stmt.Name != "" {
Expand All @@ -57,20 +57,20 @@ func GetPreparedStmt(stmt *ast.ExecuteStmt, vars *variable.SessionVars) (ast.Stm
if !ok {
return nil, errors.Errorf("invalid CachedPrepareStmt type")
}
return preparedObj.PreparedAst.Stmt, nil
return preparedObj, nil
}
return nil, plannercore.ErrStmtNotFound
}

// IsReadOnly check whether the ast.Node is a read only statement.
func IsReadOnly(node ast.Node, vars *variable.SessionVars) bool {
if execStmt, isExecStmt := node.(*ast.ExecuteStmt); isExecStmt {
s, err := GetPreparedStmt(execStmt, vars)
prepareStmt, err := GetPreparedStmt(execStmt, vars)
if err != nil {
logutil.BgLogger().Warn("GetPreparedStmt failed", zap.Error(err))
return false
}
return ast.IsReadOnly(s)
return ast.IsReadOnly(prepareStmt.PreparedAst.Stmt)
}
return ast.IsReadOnly(node)
}
Expand Down
20 changes: 8 additions & 12 deletions server/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import (
storeerr "github.com/pingcap/tidb/store/driver/error"
"github.com/pingcap/tidb/store/tikv/util"
"github.com/pingcap/tidb/tablecodec"
tidbutil "github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/arena"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/execdetails"
Expand Down Expand Up @@ -906,14 +907,6 @@ func (cc *clientConn) ShutdownOrNotify() bool {
return false
}

func queryStrForLog(query string) string {
const size = 4096
if len(query) > size {
return query[:size] + fmt.Sprintf("(len: %d)", len(query))
}
return query
}

func errStrForLog(err error, enableRedactLog bool) string {
if enableRedactLog {
// currently, only ErrParse is considered when enableRedactLog because it may contain sensitive information like
Expand Down Expand Up @@ -1025,6 +1018,9 @@ func (cc *clientConn) dispatch(ctx context.Context, data []byte) error {
cc.lastPacket = data
cmd := data[0]
data = data[1:]
if variable.TopSQLEnabled() {
defer pprof.SetGoroutineLabels(ctx)
}
if variable.EnablePProfSQLCPU.Load() {
label := getLastStmtInConn{cc}.PProfLabel()
if len(label) > 0 {
Expand Down Expand Up @@ -2125,10 +2121,10 @@ func (cc getLastStmtInConn) String() string {
if cc.ctx.GetSessionVars().EnableRedactLog {
sql = parser.Normalize(sql)
}
return queryStrForLog(sql)
return tidbutil.QueryStrForLog(sql)
case mysql.ComStmtExecute, mysql.ComStmtFetch:
stmtID := binary.LittleEndian.Uint32(data[0:4])
return queryStrForLog(cc.preparedStmt2String(stmtID))
return tidbutil.QueryStrForLog(cc.preparedStmt2String(stmtID))
case mysql.ComStmtClose, mysql.ComStmtReset:
stmtID := binary.LittleEndian.Uint32(data[0:4])
return mysql.Command2Str[cmd] + " " + strconv.Itoa(int(stmtID))
Expand Down Expand Up @@ -2156,10 +2152,10 @@ func (cc getLastStmtInConn) PProfLabel() string {
case mysql.ComStmtReset:
return "ResetStmt"
case mysql.ComQuery, mysql.ComStmtPrepare:
return parser.Normalize(queryStrForLog(string(hack.String(data))))
return parser.Normalize(tidbutil.QueryStrForLog(string(hack.String(data))))
case mysql.ComStmtExecute, mysql.ComStmtFetch:
stmtID := binary.LittleEndian.Uint32(data[0:4])
return queryStrForLog(cc.preparedStmt2StringNoArgs(stmtID))
return tidbutil.QueryStrForLog(cc.preparedStmt2StringNoArgs(stmtID))
default:
return ""
}
Expand Down
32 changes: 28 additions & 4 deletions server/conn_stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@ import (
"github.com/pingcap/tidb/metrics"
plannercore "github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/sessionctx/variable"
storeerr "github.com/pingcap/tidb/store/driver/error"
"github.com/pingcap/tidb/store/tikv/util"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/execdetails"
"github.com/pingcap/tidb/util/hack"
"github.com/pingcap/tidb/util/topsql"
)

func (cc *clientConn) handleStmtPrepare(ctx context.Context, sql string) error {
Expand Down Expand Up @@ -265,6 +267,12 @@ func (cc *clientConn) handleStmtFetch(ctx context.Context, data []byte) (err err
return errors.Annotate(mysql.NewErr(mysql.ErrUnknownStmtHandler,
strconv.FormatUint(uint64(stmtID), 10), "stmt_fetch"), cc.preparedStmt2String(stmtID))
}
if variable.TopSQLEnabled() {
prepareObj, _ := cc.preparedStmtID2CachePreparedStmt(stmtID)
if prepareObj != nil && prepareObj.SQLDigest != nil {
topsql.AttachSQLInfo(ctx, prepareObj.NormalizedSQL, prepareObj.SQLDigest, "", nil)
}
}
sql := ""
if prepared, ok := cc.ctx.GetStatement(int(stmtID)).(*TiDBStatement); ok {
sql = prepared.sql
Expand Down Expand Up @@ -680,14 +688,30 @@ func (cc *clientConn) preparedStmt2StringNoArgs(stmtID uint32) string {
if sv == nil {
return ""
}
preparedObj, invalid := cc.preparedStmtID2CachePreparedStmt(stmtID)
if invalid {
return "invalidate CachedPrepareStmt type, ID: " + strconv.FormatUint(uint64(stmtID), 10)
}
if preparedObj == nil {
return "prepared statement not found, ID: " + strconv.FormatUint(uint64(stmtID), 10)
}
return preparedObj.PreparedAst.Stmt.Text()
}

func (cc *clientConn) preparedStmtID2CachePreparedStmt(stmtID uint32) (_ *plannercore.CachedPrepareStmt, invalid bool) {
sv := cc.ctx.GetSessionVars()
if sv == nil {
return nil, false
}
preparedPointer, ok := sv.PreparedStmts[stmtID]
if !ok {
return "prepared statement not found, ID: " + strconv.FormatUint(uint64(stmtID), 10)
// not found
return nil, false
}
preparedObj, ok := preparedPointer.(*plannercore.CachedPrepareStmt)
if !ok {
return "invalidate CachedPrepareStmt type, ID: " + strconv.FormatUint(uint64(stmtID), 10)
// invalid cache. should never happen.
return nil, true
}
preparedAst := preparedObj.PreparedAst
return preparedAst.Stmt.Text()
return preparedObj, false
}
11 changes: 8 additions & 3 deletions server/http_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/logutil"
"github.com/pingcap/tidb/util/printer"
"github.com/pingcap/tidb/util/topsql/tracecpu"
"github.com/pingcap/tidb/util/versioninfo"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/soheilhy/cmux"
Expand Down Expand Up @@ -184,7 +185,7 @@ func (s *Server) startHTTPServer() {

serverMux.HandleFunc("/debug/pprof/", pprof.Index)
serverMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
serverMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
serverMux.HandleFunc("/debug/pprof/profile", tracecpu.ProfileHTTPHandler)
serverMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
serverMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
serverMux.HandleFunc("/debug/gogc", func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -251,7 +252,7 @@ func (s *Server) startHTTPServer() {
serveError(w, http.StatusInternalServerError, fmt.Sprintf("Create zipped %s fail: %v", "profile", err))
return
}
if err := rpprof.StartCPUProfile(fw); err != nil {
if err := tracecpu.StartCPUProfile(fw); err != nil {
serveError(w, http.StatusInternalServerError,
fmt.Sprintf("Could not enable CPU profiling: %s", err))
return
Expand All @@ -261,7 +262,11 @@ func (s *Server) startHTTPServer() {
sec = 10
}
sleepWithCtx(r.Context(), time.Duration(sec)*time.Second)
rpprof.StopCPUProfile()
err = tracecpu.StopCPUProfile()
if err != nil {
serveError(w, http.StatusInternalServerError, fmt.Sprintf("Create zipped %s fail: %v", "config", err))
return
}

// dump config
fw, err = zw.Create("config")
Expand Down
Loading