Skip to content
12 changes: 12 additions & 0 deletions cmd/serv.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/pprof"

"github.com/Unknwon/com"
"github.com/dgrijalva/jwt-go"
Expand All @@ -42,6 +43,9 @@ var CmdServ = cli.Command{
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
cli.BoolFlag{
Name: "enable-pprof",
},
},
}

Expand Down Expand Up @@ -143,6 +147,14 @@ func runServ(c *cli.Context) error {
username := strings.ToLower(rr[0])
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))

if setting.EnablePprof || c.Bool("enable-pprof") {
stopCPUProfiler := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
defer func() {
stopCPUProfiler()
pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
}()
}

isWiki := false
unitType := models.UnitTypeCode
if strings.HasSuffix(reponame, ".wiki") {
Expand Down
8 changes: 7 additions & 1 deletion custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ STATIC_ROOT_PATH =
APP_DATA_PATH = data
; Application level GZIP support
ENABLE_GZIP = false
; Application profiling (memory and cpu)
; For "web" command it listens on localhost:6060
; For "serve" command it dumps to disk at PPROF_DATA_PATH as (cpuprofile|memprofile)_<username>_<temporary id>
ENABLE_PPROF = false
; PPROF_DATA_PATH, use an absolute path when you start gitea as service
PPROF_DATA_PATH = data/tmp/pprof
; Landing page, can be "home", "explore", or "organizations"
LANDING_PAGE = home
; Enables git-lfs support. true or false, default is false.
Expand Down Expand Up @@ -213,7 +219,7 @@ USER = root
PASSWD =
; For "postgres" only, either "disable", "require" or "verify-full"
SSL_MODE = disable
; For "sqlite3" and "tidb", use absolute path when you start gitea as service
; For "sqlite3" and "tidb", use an absolute path when you start gitea as service
PATH = data/gitea.db
; For "sqlite3" only. Query timeout
SQLITE_TIMEOUT = 500
Expand Down
42 changes: 42 additions & 0 deletions modules/pprof/pprof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package pprof

import (
"fmt"
"io/ioutil"
"runtime"
"runtime/pprof"

"code.gitea.io/gitea/modules/log"
)

// DumpMemProfileForUsername dumps a memory profile at pprofDataPath as memprofile_<username>_<temporary id>
func DumpMemProfileForUsername(pprofDataPath, username string) {
f, err := ioutil.TempFile(pprofDataPath, fmt.Sprintf("memprofile_%s_", username))
if err != nil {
log.GitLogger.Fatal(4, "Could not create memory profile: %v", err)
}
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.GitLogger.Fatal(4, "Could not write memory profile: %v", err)
}
}

// DumpCPUProfileForUsername dumps a CPU profile at pprofDataPath as cpuprofile_<username>_<temporary id>
// it returns the stop function which stops, writes and closes the CPU profile file
func DumpCPUProfileForUsername(pprofDataPath, username string) func() {
f, err := ioutil.TempFile(pprofDataPath, fmt.Sprintf("cpuprofile_%s_", username))
if err != nil {
log.GitLogger.Fatal(4, "Could not create cpu profile: %v", err)
}

pprof.StartCPUProfile(f)
return func() {
pprof.StopCPUProfile()
f.Close()
}
}
8 changes: 8 additions & 0 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ var (
LandingPageURL LandingPage
UnixSocketPermission uint32
EnablePprof bool
PprofDataPath string

SSH = struct {
Disabled bool `ini:"DISABLE_SSH"`
Expand Down Expand Up @@ -773,6 +774,7 @@ func NewContext() {
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(path.Join(AppWorkPath, "data/tmp/pprof"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this line you also need:

if !filepath.IsAbs(PprofDataPath) {
		PprofDataPath = filepath.Join(AppWorkPath, PprofDataPath)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, will fix that.


switch sec.Key("LANDING_PAGE").MustString("home") {
case "explore":
Expand Down Expand Up @@ -916,6 +918,12 @@ func NewContext() {
}
}

if PprofDataPath != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be: if EnablePprof {

Copy link
Contributor Author

@xor-gate xor-gate Aug 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that will not work because we always have to create it. As there are two types of options:

  • Global ENABLE_PPROF and/or
  • Per key --enable-pprof from serve (then ENABLE_PPROF is not set the path does not exist in your case)

Copy link
Member

@lafriks lafriks Aug 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Than just add code to create it in second case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have decided to create the directories in one place (currently in cmd/serve.go). Felt a little weird mkdir in setting and in serv. Check my last commit.

if err := os.MkdirAll(PprofDataPath, os.ModePerm); err != nil {
log.Fatal(4, "Failed to create '%s': %v", PprofDataPath, err)
}
}

sec = Cfg.Section("security")
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
Expand Down