You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by de...@apache.org on 2018/07/05 20:10:50 UTC

[trafficcontrol] branch master updated (68a9a2f -> 67d4df0)

This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git.


    from 68a9a2f  Add TO Go regions/name client func, test
     new deb208f  add cpu profiling process, and memory profiling and db stats endpoints to traffic_ops_golang
     new 6dce088  refactor profiling information logic for readability and to fix edge case bugs
     new de96627  only start profiling loop if profiling is enabled and location is set
     new a173e2b  fix config test issues
     new 67d4df0  stop profiling loop when profiling is switched off

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 traffic_ops/app/conf/cdn.conf                      |   3 +-
 traffic_ops/traffic_ops_golang/config/config.go    |  24 +++-
 .../traffic_ops_golang/config/config_test.go       |  14 +--
 traffic_ops/traffic_ops_golang/routes.go           |  50 +++++++++
 traffic_ops/traffic_ops_golang/routing.go          |   3 +-
 .../traffic_ops_golang/traffic_ops_golang.go       | 121 ++++++++++++++++++++-
 6 files changed, 197 insertions(+), 18 deletions(-)


[trafficcontrol] 03/05: only start profiling loop if profiling is enabled and location is set

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit de96627673d161dfffc83595a939bbc72255d89d
Author: Dylan Volz <Dy...@comcast.com>
AuthorDate: Thu Jul 5 11:08:51 2018 -0600

    only start profiling loop if profiling is enabled and location is set
---
 .../traffic_ops_golang/traffic_ops_golang.go       | 35 ++++++++++++----------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index 8080b0a..55406e2 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -147,7 +147,7 @@ func main() {
 		}
 	}()
 
-	profilingLocation, err  := getProcessedProfilingLocation(cfg.ProfilingLocation,cfg.LogLocationError)
+	profilingLocation, err := getProcessedProfilingLocation(cfg.ProfilingLocation, cfg.LogLocationError)
 	if err != nil {
 		log.Errorln("unable to determine profiling location: " + err.Error())
 	}
@@ -155,34 +155,39 @@ func main() {
 	log.Infof("profiling location: %s\n", profilingLocation)
 	log.Infof("profiling enabled set to %t\n", profiling)
 
-	continuousProfile(&profiling, &profilingLocation, cfg.Version)
+	if profiling {
+		continuousProfile(&profiling, &profilingLocation, cfg.Version)
+	}
 
 	reloadProfilingConfig := func() {
-		SetNewProfilingInfo(*configFileName,&profiling,&profilingLocation)
+		SetNewProfilingInfo(*configFileName, &profiling, &profilingLocation, cfg.Version)
 	}
 	signalReloader(unix.SIGHUP, reloadProfilingConfig)
 }
 
-func SetNewProfilingInfo(configFileName string, currentProfilingEnabled *bool, currentProfilingLocation *string) {
+func SetNewProfilingInfo(configFileName string, currentProfilingEnabled *bool, currentProfilingLocation *string, version string) {
 	newProfilingEnabled, newProfilingLocation, err := reloadProfilingInfo(configFileName)
 	if err != nil {
 		log.Errorln("reloading config: ", err.Error())
 		return
 	}
-	if *currentProfilingEnabled != newProfilingEnabled {
-		log.Infof("profiling enabled set to %t\n",newProfilingEnabled)
-		*currentProfilingEnabled = newProfilingEnabled
-	}
 	if newProfilingLocation != "" && *currentProfilingLocation != newProfilingLocation {
 		*currentProfilingLocation = newProfilingLocation
 		log.Infof("profiling location set to: %s\n", *currentProfilingLocation)
 	}
+	if *currentProfilingEnabled != newProfilingEnabled {
+		log.Infof("profiling enabled set to %t\n", newProfilingEnabled)
+		*currentProfilingEnabled = newProfilingEnabled
+		if *currentProfilingEnabled {
+			continuousProfile(currentProfilingEnabled, currentProfilingLocation, version)
+		}
+	}
 }
 
 func getProcessedProfilingLocation(rawProfilingLocation string, errorLogLocation string) (string, error) {
 	profilingLocation := os.TempDir()
 
-	if errorLogLocation != "" && errorLogLocation != log.LogLocationNull && errorLogLocation !=  log.LogLocationStderr && errorLogLocation != log.LogLocationStdout {
+	if errorLogLocation != "" && errorLogLocation != log.LogLocationNull && errorLogLocation != log.LogLocationStderr && errorLogLocation != log.LogLocationStdout {
 		errorDir := filepath.Dir(errorLogLocation)
 		if _, err := os.Stat(errorDir); err == nil {
 			profilingLocation = errorDir
@@ -209,7 +214,7 @@ func reloadProfilingInfo(configFileName string) (bool, string, error) {
 	if err != nil {
 		return false, "", err
 	}
-	profilingLocation, err := getProcessedProfilingLocation(cfg.ProfilingLocation,cfg.LogLocationError)
+	profilingLocation, err := getProcessedProfilingLocation(cfg.ProfilingLocation, cfg.LogLocationError)
 	if err != nil {
 		return false, "", err
 	}
@@ -217,9 +222,9 @@ func reloadProfilingInfo(configFileName string) (bool, string, error) {
 }
 
 func continuousProfile(profiling *bool, profilingDir *string, version string) {
-	go func() {
-		for {
-			if *profiling && *profilingDir != "" {
+	if *profiling && *profilingDir != "" {
+		go func() {
+			for {
 				now := time.Now().UTC()
 				filename := filepath.Join(*profilingDir, fmt.Sprintf("tocpu-%s-%s.pprof", version, now.Format(time.RFC3339)))
 				f, err := os.Create(filename)
@@ -233,8 +238,8 @@ func continuousProfile(profiling *bool, profilingDir *string, version string) {
 				pprof.StopCPUProfile()
 				f.Close()
 			}
-		}
-	}()
+		}()
+	}
 }
 
 func signalReloader(sig os.Signal, f func()) {


[trafficcontrol] 04/05: fix config test issues

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit a173e2b5504f1f99a2aed61bae82c57eca296b5f
Author: Dylan Volz <Dy...@comcast.com>
AuthorDate: Thu Jul 5 11:22:04 2018 -0600

    fix config test issues
---
 traffic_ops/traffic_ops_golang/config/config_test.go | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/traffic_ops/traffic_ops_golang/config/config_test.go b/traffic_ops/traffic_ops_golang/config/config_test.go
index f20763d..c254914 100644
--- a/traffic_ops/traffic_ops_golang/config/config_test.go
+++ b/traffic_ops/traffic_ops_golang/config/config_test.go
@@ -197,9 +197,9 @@ func TestLoadConfig(t *testing.T) {
 
 	// test bad paths
 	_, errs, blockStartup := LoadConfig(badPath, badPath, badPath, version)
-	exp = fmt.Sprintf("reading CDN conf '%s'", badPath)
+	exp = fmt.Sprintf("got Loading cdn config from '%s'", badPath)
 	if !strings.HasPrefix(errs[0].Error(), exp) {
-		t.Error("expected", exp, "got", err)
+		t.Error("expected", exp, "got", errs[0].Error())
 	}
 	if blockStartup != true {
 		t.Error("expected blockStartup to be true but it was ", blockStartup)
@@ -207,9 +207,9 @@ func TestLoadConfig(t *testing.T) {
 
 	// bad json in cdn.conf
 	_, errs, blockStartup = LoadConfig(badCfg, badCfg, badPath, version)
-	exp = fmt.Sprintf("unmarshalling '%s'", badCfg)
+	exp = fmt.Sprintf("got Loading cdn config from '%s': unmarshalling '%s'", badCfg, badCfg)
 	if !strings.HasPrefix(errs[0].Error(), exp) {
-		t.Error("expected", exp, "got", err)
+		t.Error("expected", exp, "got", errs[0].Error())
 	}
 	if blockStartup != true {
 		t.Error("expected blockStartup to be true but it was ", blockStartup)
@@ -219,7 +219,7 @@ func TestLoadConfig(t *testing.T) {
 	_, errs, blockStartup = LoadConfig(goodCfg, badPath, badPath, version)
 	exp = fmt.Sprintf("reading db conf '%s'", badPath)
 	if !strings.HasPrefix(errs[0].Error(), exp) {
-		t.Error("expected", exp, "got", err)
+		t.Error("expected", exp, "got", errs[0].Error())
 	}
 	if blockStartup != true {
 		t.Error("expected blockStartup to be true but it was ", blockStartup)
@@ -229,7 +229,7 @@ func TestLoadConfig(t *testing.T) {
 	_, errs, blockStartup = LoadConfig(goodCfg, badCfg, badPath, version)
 	exp = fmt.Sprintf("unmarshalling '%s'", badCfg)
 	if !strings.HasPrefix(errs[0].Error(), exp) {
-		t.Error("expected", exp, "got", err)
+		t.Error("expected", exp, "got", errs[0].Error())
 	}
 	if blockStartup != true {
 		t.Error("expected blockStartup to be true but it was ", blockStartup)
@@ -238,7 +238,7 @@ func TestLoadConfig(t *testing.T) {
 	// good cdn.conf,  good database.conf
 	cfg, errs, blockStartup = LoadConfig(goodCfg, goodDbCfg, goodRiakCfg, version)
 	if len(errs) != 0 {
-		t.Error("Good config -- unexpected error ", err)
+		t.Error("Good config -- unexpected errors: ", errs)
 	}
 	if blockStartup != false {
 		t.Error("expected blockStartup to be false but it was ", blockStartup)


[trafficcontrol] 05/05: stop profiling loop when profiling is switched off

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 67d4df00533d67a3c2429401150d71744ba06457
Author: Dylan Volz <Dy...@comcast.com>
AuthorDate: Thu Jul 5 11:40:08 2018 -0600

    stop profiling loop when profiling is switched off
---
 traffic_ops/traffic_ops_golang/config/config.go      | 2 --
 traffic_ops/traffic_ops_golang/routing.go            | 2 +-
 traffic_ops/traffic_ops_golang/traffic_ops_golang.go | 6 +++++-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/traffic_ops/traffic_ops_golang/config/config.go b/traffic_ops/traffic_ops_golang/config/config.go
index c90c4e1..218d3aa 100644
--- a/traffic_ops/traffic_ops_golang/config/config.go
+++ b/traffic_ops/traffic_ops_golang/config/config.go
@@ -129,8 +129,6 @@ func (c Config) EventLog() log.LogLocation {
 const BlockStartup = true
 const AllowStartup = false
 
-
-
 func LoadCdnConfig(cdnConfPath string) (Config, error) {
 	// load json from cdn.conf
 	confBytes, err := ioutil.ReadFile(cdnConfPath)
diff --git a/traffic_ops/traffic_ops_golang/routing.go b/traffic_ops/traffic_ops_golang/routing.go
index c675aa7..c526729 100644
--- a/traffic_ops/traffic_ops_golang/routing.go
+++ b/traffic_ops/traffic_ops_golang/routing.go
@@ -73,7 +73,7 @@ func getDefaultMiddleware(secret string) []Middleware {
 // ServerData ...
 type ServerData struct {
 	config.Config
-	DB *sqlx.DB
+	DB        *sqlx.DB
 	Profiling *bool // Yes this is a field in the config but we want to live reload this value and NOT the entire config
 }
 
diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index 55406e2..3966f0c 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -230,13 +230,17 @@ func continuousProfile(profiling *bool, profilingDir *string, version string) {
 				f, err := os.Create(filename)
 				if err != nil {
 					log.Errorf("creating profile: %v\n", err)
-					os.Exit(1)
+					log.Infof("Exiting profiling")
+					break
 				}
 
 				pprof.StartCPUProfile(f)
 				time.Sleep(time.Minute)
 				pprof.StopCPUProfile()
 				f.Close()
+				if !*profiling {
+					break
+				}
 			}
 		}()
 	}


[trafficcontrol] 01/05: add cpu profiling process, and memory profiling and db stats endpoints to traffic_ops_golang

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit deb208f0a73c1a62fae38d1fd674d6caaebc959c
Author: Dylan Volz <Dy...@comcast.com>
AuthorDate: Mon Jul 2 10:18:54 2018 -0600

    add cpu profiling process, and memory profiling and db stats endpoints to traffic_ops_golang
---
 traffic_ops/app/conf/cdn.conf                      |  3 +-
 traffic_ops/traffic_ops_golang/config/config.go    | 24 ++++--
 traffic_ops/traffic_ops_golang/routes.go           | 50 ++++++++++++
 traffic_ops/traffic_ops_golang/routing.go          |  1 +
 .../traffic_ops_golang/traffic_ops_golang.go       | 91 +++++++++++++++++++++-
 5 files changed, 159 insertions(+), 10 deletions(-)

diff --git a/traffic_ops/app/conf/cdn.conf b/traffic_ops/app/conf/cdn.conf
index 7f1a13f..d365c3a 100644
--- a/traffic_ops/app/conf/cdn.conf
+++ b/traffic_ops/app/conf/cdn.conf
@@ -27,7 +27,8 @@
         "max_db_connections": 20,
         "backend_max_connections": {
             "mojolicious": 4
-        }
+        },
+	"profiling_enabled": false
     },
     "cors" : {
         "access_control_allow_origin" : "*"
diff --git a/traffic_ops/traffic_ops_golang/config/config.go b/traffic_ops/traffic_ops_golang/config/config.go
index b47d402..c90c4e1 100644
--- a/traffic_ops/traffic_ops_golang/config/config.go
+++ b/traffic_ops/traffic_ops_golang/config/config.go
@@ -75,6 +75,8 @@ type ConfigTrafficOpsGolang struct {
 	Insecure               bool           `json:"insecure"`
 	MaxDBConnections       int            `json:"max_db_connections"`
 	BackendMaxConnections  map[string]int `json:"backend_max_connections"`
+	ProfilingEnabled       bool           `json:"profiling_enabled"`
+	ProfilingLocation      string         `json:"profiling_location"`
 }
 
 // ConfigDatabase reflects the structure of the database.conf file
@@ -127,20 +129,32 @@ func (c Config) EventLog() log.LogLocation {
 const BlockStartup = true
 const AllowStartup = false
 
-// LoadConfig - reads the config file into the Config struct
 
-func LoadConfig(cdnConfPath string, dbConfPath string, riakConfPath string, appVersion string) (Config, []error, bool) {
+
+func LoadCdnConfig(cdnConfPath string) (Config, error) {
 	// load json from cdn.conf
 	confBytes, err := ioutil.ReadFile(cdnConfPath)
 	if err != nil {
-		return Config{}, []error{fmt.Errorf("reading CDN conf '%s': %v", cdnConfPath, err)}, BlockStartup
+		return Config{}, fmt.Errorf("reading CDN conf '%s': %v", cdnConfPath, err)
 	}
 
-	cfg := Config{Version: appVersion}
+	cfg := Config{}
 	err = json.Unmarshal(confBytes, &cfg)
 	if err != nil {
-		return Config{}, []error{fmt.Errorf("unmarshalling '%s': %v", cdnConfPath, err)}, BlockStartup
+		return Config{}, fmt.Errorf("unmarshalling '%s': %v", cdnConfPath, err)
+	}
+	return cfg, nil
+}
+
+// LoadConfig - reads the config file into the Config struct
+
+func LoadConfig(cdnConfPath string, dbConfPath string, riakConfPath string, appVersion string) (Config, []error, bool) {
+	// load cdn.conf
+	cfg, err := LoadCdnConfig(cdnConfPath)
+	if err != nil {
+		return Config{}, []error{fmt.Errorf("Loading cdn config from '%s': %v", cdnConfPath, err)}, BlockStartup
 	}
+	cfg.Version = appVersion
 
 	// load json from database.conf
 	dbConfBytes, err := ioutil.ReadFile(dbConfPath)
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index dbff573..33acff1 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -21,11 +21,14 @@ package main
 
 import (
 	"crypto/tls"
+	"encoding/json"
+	"errors"
 	"io"
 	"log"
 	"net"
 	"net/http"
 	"net/http/httputil"
+	"runtime"
 	"time"
 
 	tclog "github.com/apache/trafficcontrol/lib/go-log"
@@ -61,6 +64,7 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/types"
 
 	"github.com/basho/riak-go-client"
+	"github.com/jmoiron/sqlx"
 )
 
 // Authenticated ...
@@ -354,11 +358,57 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
 		{http.MethodGet, `tools/write_crconfig/{cdn}/?$`, crconfig.SnapshotOldGUIHandler(d.DB, d.Config), auth.PrivLevelOperations, Authenticated, nil},
 		// DEPRECATED - use GET /api/1.2/cdns/{cdn}/snapshot
 		{http.MethodGet, `CRConfig-Snapshots/{cdn}/CRConfig.json?$`, crconfig.SnapshotOldGetHandler(d.DB, d.Config), auth.PrivLevelReadOnly, Authenticated, nil},
+		// USED FOR Debugging
+		{http.MethodGet, `admin/memory-stats`, memoryStatsHandler(d.Profiling), auth.PrivLevelOperations, Authenticated, nil},
+		{http.MethodGet, `admin/db-stats`, dbStatsHandler(d.Profiling, d.DB), auth.PrivLevelOperations, Authenticated, nil},
 	}
 
 	return routes, rawRoutes, proxyHandler, nil
 }
 
+func memoryStatsHandler(profiling *bool) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		handleErrs := tc.GetHandleErrorsFunc(w, r)
+		if *profiling {
+			stats := runtime.MemStats{}
+			runtime.ReadMemStats(&stats)
+
+			bytes, err := json.Marshal(stats)
+			if err != nil {
+				tclog.Errorln("unable to marshal stats: " + err.Error())
+				handleErrs(http.StatusInternalServerError, errors.New("marshalling error"))
+				return
+			}
+			w.Header().Set("Content-Type", "application/json")
+			w.Write(bytes)
+		} else {
+			handleErrs(http.StatusPreconditionFailed, errors.New("profiling is not enabled"))
+			return
+		}
+	}
+}
+
+func dbStatsHandler(profiling *bool, db *sqlx.DB) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		handleErrs := tc.GetHandleErrorsFunc(w, r)
+		if *profiling {
+			stats := db.DB.Stats()
+
+			bytes, err := json.Marshal(stats)
+			if err != nil {
+				tclog.Errorln("unable to marshal stats: " + err.Error())
+				handleErrs(http.StatusInternalServerError, errors.New("marshalling error"))
+				return
+			}
+			w.Header().Set("Content-Type", "application/json")
+			w.Write(bytes)
+		} else {
+			handleErrs(http.StatusPreconditionFailed, errors.New("profiling is not enabled"))
+			return
+		}
+	}
+}
+
 // RootHandler returns the / handler for the service, which reverse-proxies the old Perl Traffic Ops
 func rootHandler(d ServerData) http.Handler {
 	tr := &http.Transport{
diff --git a/traffic_ops/traffic_ops_golang/routing.go b/traffic_ops/traffic_ops_golang/routing.go
index 24abbdb..c675aa7 100644
--- a/traffic_ops/traffic_ops_golang/routing.go
+++ b/traffic_ops/traffic_ops_golang/routing.go
@@ -74,6 +74,7 @@ func getDefaultMiddleware(secret string) []Middleware {
 type ServerData struct {
 	config.Config
 	DB *sqlx.DB
+	Profiling *bool // Yes this is a field in the config but we want to live reload this value and NOT the entire config
 }
 
 // CompiledRoute ...
diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index f5e2865..15644d3 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -25,14 +25,19 @@ import (
 	"fmt"
 	"net/http"
 	"os"
+	"runtime/pprof"
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/about"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
 
+	"os/signal"
+	"path/filepath"
+
 	"github.com/jmoiron/sqlx"
 	_ "github.com/lib/pq"
+	"golang.org/x/sys/unix"
 )
 
 // set the version at build time: `go build -X "main.version=..."`
@@ -117,7 +122,9 @@ func main() {
 
 	db.SetMaxOpenConns(cfg.MaxDBConnections)
 
-	if err := RegisterRoutes(ServerData{DB: db, Config: cfg}); err != nil {
+	profiling := cfg.ProfilingEnabled
+
+	if err := RegisterRoutes(ServerData{DB: db, Config: cfg, Profiling: &profiling}); err != nil {
 		log.Errorf("registering routes: %v\n", err)
 		return
 	}
@@ -134,8 +141,84 @@ func main() {
 		ErrorLog:          log.Error,
 	}
 
-	if err := server.ListenAndServeTLS(cfg.CertPath, cfg.KeyPath); err != nil {
-		log.Errorf("stopping server: %v\n", err)
-		return
+	go func() {
+		if err := server.ListenAndServeTLS(cfg.CertPath, cfg.KeyPath); err != nil {
+			log.Errorf("stopping server: %v\n", err)
+			panic(err)
+		}
+	}()
+
+	profilingLocation := os.TempDir()
+
+	if cfg.LogLocationError != "" && cfg.LogLocationError != "stdout" {
+		errorDir := filepath.Dir(cfg.LogLocationError)
+
+		if _, err := os.Stat(errorDir); err == nil {
+			profilingLocation = errorDir
+		}
+	}
+
+	profilingLocation = filepath.Join(profilingLocation, "profiling")
+	if cfg.ProfilingLocation != "" {
+		profilingLocation = cfg.ProfilingLocation
+	} else {
+		//if it isn't a provided location create the profiling directory under the default temp location
+		err = os.Mkdir(profilingLocation, 0755)
+		if err != nil {
+			log.Errorf("unable to create profiling location: %s\n", err.Error())
+		}
+	}
+
+	reloadProfilingConfig := func() {
+		log.Debugln("received SIGHUP")
+		newCfg, err := config.LoadCdnConfig(*configFileName)
+		if err != nil {
+			log.Errorln("reloading config: ", err.Error())
+		}
+		profiling = newCfg.ProfilingEnabled
+		if newCfg.ProfilingLocation != "" {
+			profilingLocation = cfg.ProfilingLocation
+			log.Infof("profiling location: %s\n", profilingLocation)
+		}
+		if profiling {
+			log.Infoln("profiling enabled")
+		}
+	}
+
+	log.Infof("profiling location: %s\n", profilingLocation)
+	if profiling {
+		log.Infoln("profiling enabled")
+	}
+	continuousProfile(&profiling, &profilingLocation, cfg.Version)
+
+	signalReloader(unix.SIGHUP, reloadProfilingConfig)
+}
+
+func continuousProfile(profiling *bool, profilingDir *string, version string) {
+	go func() {
+		for {
+			if *profiling {
+				now := time.Now().UTC()
+				filename := filepath.Join(*profilingDir, fmt.Sprintf("tocpu-%s-%s.pprof", version, now.Format(time.RFC3339)))
+				f, err := os.Create(filename)
+				if err != nil {
+					log.Errorf("creating profile: %v\n", err)
+					os.Exit(1)
+				}
+
+				pprof.StartCPUProfile(f)
+				time.Sleep(time.Minute)
+				pprof.StopCPUProfile()
+				f.Close()
+			}
+		}
+	}()
+}
+
+func signalReloader(sig os.Signal, f func()) {
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, sig)
+	for range c {
+		f()
 	}
 }


[trafficcontrol] 02/05: refactor profiling information logic for readability and to fix edge case bugs

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dewrich pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 6dce0881a0d54a345d62b9b18941aad72812a3b0
Author: Dylan Volz <Dy...@comcast.com>
AuthorDate: Tue Jul 3 13:31:07 2018 -0600

    refactor profiling information logic for readability and to fix edge case bugs
---
 .../traffic_ops_golang/traffic_ops_golang.go       | 91 ++++++++++++++--------
 1 file changed, 57 insertions(+), 34 deletions(-)

diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index 15644d3..8080b0a 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -25,6 +25,8 @@ import (
 	"fmt"
 	"net/http"
 	"os"
+	"os/signal"
+	"path/filepath"
 	"runtime/pprof"
 	"time"
 
@@ -32,9 +34,6 @@ import (
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/about"
 	"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
 
-	"os/signal"
-	"path/filepath"
-
 	"github.com/jmoiron/sqlx"
 	_ "github.com/lib/pq"
 	"golang.org/x/sys/unix"
@@ -148,56 +147,79 @@ func main() {
 		}
 	}()
 
-	profilingLocation := os.TempDir()
+	profilingLocation, err  := getProcessedProfilingLocation(cfg.ProfilingLocation,cfg.LogLocationError)
+	if err != nil {
+		log.Errorln("unable to determine profiling location: " + err.Error())
+	}
+
+	log.Infof("profiling location: %s\n", profilingLocation)
+	log.Infof("profiling enabled set to %t\n", profiling)
+
+	continuousProfile(&profiling, &profilingLocation, cfg.Version)
+
+	reloadProfilingConfig := func() {
+		SetNewProfilingInfo(*configFileName,&profiling,&profilingLocation)
+	}
+	signalReloader(unix.SIGHUP, reloadProfilingConfig)
+}
+
+func SetNewProfilingInfo(configFileName string, currentProfilingEnabled *bool, currentProfilingLocation *string) {
+	newProfilingEnabled, newProfilingLocation, err := reloadProfilingInfo(configFileName)
+	if err != nil {
+		log.Errorln("reloading config: ", err.Error())
+		return
+	}
+	if *currentProfilingEnabled != newProfilingEnabled {
+		log.Infof("profiling enabled set to %t\n",newProfilingEnabled)
+		*currentProfilingEnabled = newProfilingEnabled
+	}
+	if newProfilingLocation != "" && *currentProfilingLocation != newProfilingLocation {
+		*currentProfilingLocation = newProfilingLocation
+		log.Infof("profiling location set to: %s\n", *currentProfilingLocation)
+	}
+}
 
-	if cfg.LogLocationError != "" && cfg.LogLocationError != "stdout" {
-		errorDir := filepath.Dir(cfg.LogLocationError)
+func getProcessedProfilingLocation(rawProfilingLocation string, errorLogLocation string) (string, error) {
+	profilingLocation := os.TempDir()
 
+	if errorLogLocation != "" && errorLogLocation != log.LogLocationNull && errorLogLocation !=  log.LogLocationStderr && errorLogLocation != log.LogLocationStdout {
+		errorDir := filepath.Dir(errorLogLocation)
 		if _, err := os.Stat(errorDir); err == nil {
 			profilingLocation = errorDir
 		}
 	}
 
 	profilingLocation = filepath.Join(profilingLocation, "profiling")
-	if cfg.ProfilingLocation != "" {
-		profilingLocation = cfg.ProfilingLocation
+	if rawProfilingLocation != "" {
+		profilingLocation = rawProfilingLocation
 	} else {
-		//if it isn't a provided location create the profiling directory under the default temp location
-		err = os.Mkdir(profilingLocation, 0755)
-		if err != nil {
-			log.Errorf("unable to create profiling location: %s\n", err.Error())
+		//if it isn't a provided location create the profiling directory under the default temp location if it doesn't exist
+		if _, err := os.Stat(profilingLocation); err != nil {
+			err = os.Mkdir(profilingLocation, 0755)
+			if err != nil {
+				return "", fmt.Errorf("unable to create profiling location: %s\n", err.Error())
+			}
 		}
 	}
+	return profilingLocation, nil
+}
 
-	reloadProfilingConfig := func() {
-		log.Debugln("received SIGHUP")
-		newCfg, err := config.LoadCdnConfig(*configFileName)
-		if err != nil {
-			log.Errorln("reloading config: ", err.Error())
-		}
-		profiling = newCfg.ProfilingEnabled
-		if newCfg.ProfilingLocation != "" {
-			profilingLocation = cfg.ProfilingLocation
-			log.Infof("profiling location: %s\n", profilingLocation)
-		}
-		if profiling {
-			log.Infoln("profiling enabled")
-		}
+func reloadProfilingInfo(configFileName string) (bool, string, error) {
+	cfg, err := config.LoadCdnConfig(configFileName)
+	if err != nil {
+		return false, "", err
 	}
-
-	log.Infof("profiling location: %s\n", profilingLocation)
-	if profiling {
-		log.Infoln("profiling enabled")
+	profilingLocation, err := getProcessedProfilingLocation(cfg.ProfilingLocation,cfg.LogLocationError)
+	if err != nil {
+		return false, "", err
 	}
-	continuousProfile(&profiling, &profilingLocation, cfg.Version)
-
-	signalReloader(unix.SIGHUP, reloadProfilingConfig)
+	return cfg.ProfilingEnabled, profilingLocation, nil
 }
 
 func continuousProfile(profiling *bool, profilingDir *string, version string) {
 	go func() {
 		for {
-			if *profiling {
+			if *profiling && *profilingDir != "" {
 				now := time.Now().UTC()
 				filename := filepath.Join(*profilingDir, fmt.Sprintf("tocpu-%s-%s.pprof", version, now.Format(time.RFC3339)))
 				f, err := os.Create(filename)
@@ -219,6 +241,7 @@ func signalReloader(sig os.Signal, f func()) {
 	c := make(chan os.Signal, 1)
 	signal.Notify(c, sig)
 	for range c {
+		log.Debugln("received SIGHUP")
 		f()
 	}
 }