Add support for profiling
[pstop.git] / main.go
1 // Top like progream which collects information from MySQL's
2 // performance_schema database.
3 package main
4
5 import (
6         "database/sql"
7         "flag"
8         "fmt"
9         "log"
10         "os"
11         "os/signal"
12         "runtime/pprof"
13         "syscall"
14         "time"
15
16         _ "github.com/go-sql-driver/mysql"
17         "github.com/nsf/termbox-go"
18
19         "github.com/sjmudd/mysql_defaults_file"
20         "github.com/sjmudd/pstop/lib"
21         "github.com/sjmudd/pstop/state"
22         "github.com/sjmudd/pstop/version"
23 )
24
25 const (
26         sql_driver = "mysql"
27         db         = "performance_schema"
28 )
29
30 var flag_version = flag.Bool("version", false, "Show the version of "+lib.MyName())
31 var flag_debug = flag.Bool("debug", false, "Enabling debug logging")
32 var flag_help = flag.Bool("help", false, "Provide some help for "+lib.MyName())
33 var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
34
35 func get_db_handle() *sql.DB {
36         var err error
37         var dbh *sql.DB
38
39         dbh, err = mysql_defaults_file.OpenUsingDefaultsFile(sql_driver, "", "performance_schema")
40         if err != nil {
41                 log.Fatal(err)
42         }
43         if err = dbh.Ping(); err != nil {
44                 log.Fatal(err)
45         }
46
47         return dbh
48 }
49
50 // make chan for termbox events and run a poller to send events to the channel
51 // - return the channel
52 func new_tb_chan() chan termbox.Event {
53         termboxChan := make(chan termbox.Event)
54         go func() {
55                 for {
56                         termboxChan <- termbox.PollEvent()
57                 }
58         }()
59         return termboxChan
60 }
61
62 func usage() {
63         fmt.Println(lib.MyName() + " - " + lib.Copyright())
64         fmt.Println("")
65         fmt.Println("Top-like program to show MySQL activity by using information collected")
66         fmt.Println("from performance_schema.")
67         fmt.Println("")
68         fmt.Println("Usage: " + lib.MyName() + " <options>")
69         fmt.Println("")
70         fmt.Println("Options:")
71         fmt.Println("-help      show this help message")
72         fmt.Println("-version   show the version")
73 }
74
75 func main() {
76         flag.Parse()
77
78         // clean me up
79         if *cpuprofile != "" {
80                 f, err := os.Create(*cpuprofile)
81                 if err != nil {
82                         log.Fatal(err)
83                 }
84                 pprof.StartCPUProfile(f)
85                 defer pprof.StopCPUProfile()
86         }
87
88         if *flag_debug {
89                 lib.Logger.EnableLogging(true)
90         }
91         if *flag_version {
92                 fmt.Println(lib.MyName() + " version " + version.Version())
93                 return
94         }
95         if *flag_help {
96                 usage()
97                 return
98         }
99
100         lib.Logger.Println("Starting " + lib.MyName())
101         var state state.State
102
103         interval := time.Second
104         sigChan := make(chan os.Signal, 1)
105         done := make(chan struct{})
106         defer close(done)
107         termboxChan := new_tb_chan()
108
109         signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
110
111         ticker := time.NewTicker(interval) // generate a periodic signal
112
113         state.Setup(get_db_handle())
114
115         finished := false
116         for !finished {
117                 select {
118                 case <-done:
119                         fmt.Println("exiting")
120                         finished = true
121                 case sig := <-sigChan:
122                         fmt.Println("Caught a signal", sig)
123                         done <- struct{}{}
124                 case <-ticker.C:
125                         state.Collect()
126                         state.Display()
127                 case event := <-termboxChan:
128                         // switch on event type
129                         switch event.Type {
130                         case termbox.EventKey: // actions depend on key
131                                 switch event.Key {
132                                 case termbox.KeyCtrlZ, termbox.KeyCtrlC, termbox.KeyEsc:
133                                         finished = true
134                                 case termbox.KeyTab: // tab - change display modes
135                                         state.DisplayNext()
136                                         state.Display()
137                                 }
138                                 switch event.Ch {
139                                 case '-': // decrease the interval if > 1
140                                         if interval > time.Second {
141                                                 ticker.Stop()
142                                                 interval -= time.Second
143                                                 ticker = time.NewTicker(interval)
144                                         }
145                                 case '+': // increase interval by creating a new ticker
146                                         ticker.Stop()
147                                         interval += time.Second
148                                         ticker = time.NewTicker(interval)
149                                 case 'h': // help
150                                         state.SetHelp(!state.Help())
151                                 case 'q': // quit
152                                         finished = true
153                                 case 't': // toggle between absolute/relative statistics
154                                         state.SetWantRelativeStats(!state.WantRelativeStats())
155                                         state.Display()
156                                 case 'z': // reset the statistics to now by taking a query of current values
157                                         state.ResetDBStatistics()
158                                         state.Display()
159                                 }
160                         case termbox.EventResize: // set sizes
161                                 state.ScreenSetSize(event.Width, event.Height)
162                                 state.Display()
163                         case termbox.EventError: // quit
164                                 log.Fatalf("Quitting because of termbox error: \n%s\n", event.Err)
165                         }
166                 }
167         }
168         state.Cleanup()
169         ticker.Stop()
170         lib.Logger.Println("Terminating " + lib.MyName())
171 }