Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: common/runtime/profiling/profiler.go

Issue 2548253004: Add profiling, integrate into Butler/Annotee. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | logdog/client/cmd/logdog_annotee/main.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package profiling
6
7 import (
8 "flag"
9 "fmt"
10 "net"
11 "net/http"
12 httpProf "net/http/pprof"
13 "os"
14 "path/filepath"
15 "runtime"
16 "runtime/pprof"
17 "sync/atomic"
18
19 "github.com/julienschmidt/httprouter"
20 "github.com/luci/luci-go/common/clock"
21 "github.com/luci/luci-go/common/errors"
22 "github.com/luci/luci-go/common/logging"
23 )
24
25 // Profiler helps setup and manage profiling
26 type Profiler struct {
27 // BindHTTP, if not empty, is the HTTP address to bind to.
28 //
29 // Can also be configured with "-profile-bind-http" flag.
30 BindHTTP string
31
32 // Dir, if set, is the path where profiling data will be written to.
33 //
34 // Can also be configured with "-profile-output-dir" flag.
35 Dir string
36
37 // Logger, if not nil, will be used to log events and errors. If nil, no
38 // logging will be used.
39 Logger logging.Logger
40 // Clock is the clock instance to use. If nil, the system clock will be used.
41 Clock clock.Clock
42
43 // listener is the active listener instance. It is set when Start is cal led.
44 listener net.Listener
45
46 // pathCounter is an atomic counter used to ensure non-conflicting paths .
47 pathCounter uint32
48 }
49
50 // AddFlags adds command line flags to common Profiler fields.
51 func (p *Profiler) AddFlags(fs *flag.FlagSet) {
52 fs.StringVar(&p.BindHTTP, "profile-bind-http", "",
53 "If specified, run a runtime profiler HTTP server bound to this [address][:port].")
54 fs.StringVar(&p.Dir, "profile-output-dir", "",
55 "If specified, allow generation of profiling artifacts, which wi ll be written here.")
56 }
57
58 // Start starts the Profiler's configured operations. On success, returns a
59 // function that can be called to shutdown the profiling server.
60 //
61 // Calling Stop is not necessary, but will enable end-of-operation profiling
62 // to be gathered.
63 func (p *Profiler) Start() error {
64 // If we have an output directory, start our CPU profiling.
65 if p.BindHTTP != "" {
66 if err := p.startHTTP(); err != nil {
67 return errors.Annotate(err).Reason("failed to start HTTP server").Err()
68 }
69 }
70 return nil
71 }
72
73 func (p *Profiler) startHTTP() error {
74 // Register paths: https://golang.org/src/net/http/pprof/pprof.go
75 router := httprouter.New()
76 router.HandlerFunc("GET", "/debug/pprof/", httpProf.Index)
77 router.HandlerFunc("GET", "/debug/pprof/cmdline", httpProf.Cmdline)
78 router.HandlerFunc("GET", "/debug/pprof/profile", httpProf.Profile)
79 router.HandlerFunc("GET", "/debug/pprof/symbol", httpProf.Symbol)
80 router.HandlerFunc("GET", "/debug/pprof/trace", httpProf.Trace)
81 for _, p := range pprof.Profiles() {
82 name := p.Name()
83 router.Handler("GET", fmt.Sprintf("/debug/pprof/%s", name), http Prof.Handler(name))
84 }
85
86 // Bind to our profiling port.
87 l, err := net.Listen("tcp4", p.BindHTTP)
88 if err != nil {
89 return errors.Annotate(err).Reason("failed to bind to TCP4 addre ss: %(addr)q").
90 D("addr", p.BindHTTP).Err()
91 }
92
93 server := http.Server{
94 Handler: http.HandlerFunc(router.ServeHTTP),
95 }
96 go func() {
97 if err := server.Serve(l); err != nil {
98 p.getLogger().Errorf("Error serving profile HTTP: %s", e rr)
99 }
100 }()
101 return nil
102 }
103
104 // Stop stops the Profiler's operations.
105 func (p *Profiler) Stop() {
106 if p.listener != nil {
107 if err := p.listener.Close(); err != nil {
108 p.getLogger().Warningf("Failed to stop profile HTTP serv er: %s", err)
109 }
110 p.listener = nil
111 }
112
113 // Take one final snapshot.
114 p.DumpSnapshot()
115 }
116
117 // DumpSnapshot dumps a profile snapshot to the configured output directory. If
118 // no output directory is configured, nothing wil happen.
119 func (p *Profiler) DumpSnapshot() error {
120 if p.Dir == "" {
121 return nil
122 }
123
124 if err := p.dumpHeapProfile(); err != nil {
125 return errors.Annotate(err).Reason("failed to dump heap profile" ).Err()
126 }
127 return nil
128 }
129
130 func (p *Profiler) dumpHeapProfile() error {
131 fd, err := os.Create(p.generateOutPath("memory"))
132 if err != nil {
133 return errors.Annotate(err).Reason("failed to create output file ").Err()
134 }
135 defer fd.Close()
136
137 // Get up-to-date statistics.
138 runtime.GC()
139 if err := pprof.WriteHeapProfile(fd); err != nil {
140 return errors.Annotate(err).Reason("failed to write heap profile ").Err()
141 }
142 return nil
143 }
144
145 func (p *Profiler) generateOutPath(base string) string {
146 clk := p.Clock
147 if clk == nil {
148 clk = clock.GetSystemClock()
149 }
150 now := clk.Now()
151 counter := atomic.AddUint32(&p.pathCounter, 1) - 1
152 return filepath.Join(p.Dir, fmt.Sprintf("%s_%d_%d.prof", base, now.Unix( ), counter))
153 }
154
155 func (p *Profiler) getLogger() logging.Logger {
156 if p.Logger != nil {
157 return p.Logger
158 }
159 return logging.Null
160 }
OLDNEW
« no previous file with comments | « no previous file | logdog/client/cmd/logdog_annotee/main.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698