1 // lib - library routines for pstop.
3 // this file contains the library routines related to the stored state in pstop.
12 "github.com/sjmudd/pstop/lib"
13 fsbi "github.com/sjmudd/pstop/performance_schema/file_summary_by_instance"
14 "github.com/sjmudd/pstop/performance_schema/ps_table"
15 tiwsbt "github.com/sjmudd/pstop/performance_schema/table_io_waits_summary_by_table"
16 tlwsbt "github.com/sjmudd/pstop/performance_schema/table_lock_waits_summary_by_table"
17 "github.com/sjmudd/pstop/screen"
18 "github.com/sjmudd/pstop/version"
21 // what information to show
36 fsbi ps_table.Tabler // ufsbi.File_summary_by_instance
37 tiwsbt tiwsbt.Table_io_waits_summary_by_table
38 tlwsbt ps_table.Tabler // tlwsbt.Table_lock_waits_summary_by_table
39 screen screen.TermboxScreen
42 want_relative_stats bool
45 func (state *State) Setup(dbh *sql.DB) {
48 state.screen.Initialise()
50 _, variables := lib.SelectAllGlobalVariablesByVariableName(state.dbh)
51 // setup to their initial types/values
52 state.fsbi = fsbi.NewFileSummaryByInstance(variables)
53 state.tlwsbt = new(tlwsbt.Table_lock_waits_summary_by_table)
55 state.want_relative_stats = true // we show info from the point we start collecting data
56 state.fsbi.SetWantRelativeStats(state.want_relative_stats)
58 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
60 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
63 state.ResetDBStatistics()
66 state.show = showLatency
67 state.tiwsbt.SetWantsLatency(true)
69 // get short name (to save space)
70 _, hostname := lib.SelectGlobalVariableByVariableName(state.dbh, "HOSTNAME")
71 if index := strings.Index(hostname, "."); index >= 0 {
72 hostname = hostname[0:index]
74 _, mysql_version := lib.SelectGlobalVariableByVariableName(state.dbh, "VERSION")
75 _, datadir := lib.SelectGlobalVariableByVariableName(state.dbh, "DATADIR")
76 state.SetHostname(hostname)
77 state.SetMySQLVersion(mysql_version)
78 state.SetDatadir(datadir)
81 // do a fresh collection of data and then update the initial values based on that.
82 func (state *State) ResetDBStatistics() {
84 state.SyncReferenceValues()
87 func (state *State) SyncReferenceValues() {
89 state.fsbi.SyncReferenceValues()
90 state.tlwsbt.SyncReferenceValues()
91 state.tiwsbt.SyncReferenceValues()
92 lib.Logger.Println("state.SyncReferenceValues() took", time.Duration(time.Since(start)).String())
95 // collect all initial values on startup / reset
96 func (state *State) CollectAll() {
97 state.fsbi.Collect(state.dbh)
98 state.tlwsbt.Collect(state.dbh)
99 state.tiwsbt.Collect(state.dbh)
102 // Only collect the data we are looking at.
103 func (state *State) Collect() {
107 case showLatency, showOps:
108 state.tiwsbt.Collect(state.dbh)
110 state.fsbi.Collect(state.dbh)
112 state.tlwsbt.Collect(state.dbh)
114 lib.Logger.Println("state.Collect() took", time.Duration(time.Since(start)).String())
117 func (state State) MySQLVersion() string {
118 return state.mysql_version
121 func (state State) Datadir() string {
125 func (state *State) SetHelp(newHelp bool) {
132 func (state *State) SetDatadir(datadir string) {
133 state.datadir = datadir
136 func (state *State) SetMySQLVersion(mysql_version string) {
137 state.mysql_version = mysql_version
140 func (state *State) SetHostname(hostname string) {
141 state.hostname = hostname
144 func (state State) Help() bool {
148 // display the output according to the mode we are in
149 func (state *State) Display() {
151 state.screen.DisplayHelp()
153 state.displayHeading()
155 case showLatency, showOps:
156 state.displayOpsOrLatency()
165 // change to the next display mode
166 func (state *State) DisplayNext() {
167 if state.show == showLocks {
168 state.show = showLatency
172 // this needs to be done more cleanly
173 if state.show == showLatency {
174 state.tiwsbt.SetWantsLatency(true)
176 if state.show == showOps {
177 state.tiwsbt.SetWantsLatency(false)
183 func (state State) displayHeading() {
185 state.displayDescription()
188 func (state State) displayLine0() {
189 _, uptime := lib.SelectGlobalStatusByVariableName(state.dbh, "UPTIME")
190 top_line := lib.MyName() + " " + version.Version() + " - " + now_hhmmss() + " " + state.hostname + " / " + state.mysql_version + ", up " + fmt.Sprintf("%-16s", lib.Uptime(uptime))
191 if state.want_relative_stats {
194 var initial time.Time
197 case showLatency, showOps:
198 initial = state.tiwsbt.Last()
200 initial = state.fsbi.Last()
202 initial = state.tlwsbt.Last()
204 initial = time.Now() // THIS IS WRONG !!!
207 d := now.Sub(initial)
209 top_line = top_line + " [REL] " + fmt.Sprintf("%.0f seconds", d.Seconds())
211 top_line = top_line + " [ABS] "
213 state.screen.PrintAt(0, 0, top_line)
216 func (state State) displayDescription() {
217 description := "UNKNOWN"
220 case showLatency, showOps:
221 description = state.tiwsbt.Description()
223 description = state.fsbi.Description()
225 description = state.tlwsbt.Description()
228 state.screen.PrintAt(0, 1, description)
231 func (state *State) displayOpsOrLatency() {
232 state.screen.PrintAt(0, 2, state.tiwsbt.Headings())
234 max_rows := state.screen.Height() - 3
235 row_content := state.tiwsbt.RowContent(max_rows)
238 for k := range row_content {
240 state.screen.PrintAt(0, y, row_content[k])
242 // print out empty rows
243 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
245 if y < state.screen.Height()-1 {
246 state.screen.PrintAt(0, y, state.tiwsbt.EmptyRowContent())
250 // print out the totals at the bottom
251 state.screen.PrintAt(0, state.screen.Height()-1, state.tiwsbt.TotalRowContent())
254 // show actual I/O latency values
255 func (state State) displayIO() {
256 state.screen.PrintAt(0, 2, state.fsbi.Headings())
258 // print out the data
259 max_rows := state.screen.Height() - 3
260 row_content := state.fsbi.RowContent(max_rows)
263 for k := range row_content {
265 state.screen.PrintAt(0, y, row_content[k])
267 // print out empty rows
268 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
270 if y < state.screen.Height()-1 {
271 state.screen.PrintAt(0, y, state.fsbi.EmptyRowContent())
275 // print out the totals at the bottom
276 state.screen.PrintAt(0, state.screen.Height()-1, state.fsbi.TotalRowContent())
279 func (state *State) displayLocks() {
280 state.screen.PrintAt(0, 2, state.tlwsbt.Headings())
282 // print out the data
283 max_rows := state.screen.Height() - 3
284 row_content := state.tlwsbt.RowContent(max_rows)
287 for k := range row_content {
289 state.screen.PrintAt(0, y, row_content[k])
291 // print out empty rows
292 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
294 if y < state.screen.Height()-1 {
295 state.screen.PrintAt(0, y, state.tlwsbt.EmptyRowContent())
299 // print out the totals at the bottom
300 state.screen.PrintAt(0, state.screen.Height()-1, state.tlwsbt.TotalRowContent())
303 // do we want to show all p_s data?
304 func (state State) WantRelativeStats() bool {
305 return state.want_relative_stats
308 // set if we want data from when we started/reset stats.
309 func (state *State) SetWantRelativeStats(want_relative_stats bool) {
310 state.want_relative_stats = want_relative_stats
312 state.fsbi.SetWantRelativeStats(want_relative_stats)
313 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
314 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
317 // if there's a better way of doing this do it better ...
318 func now_hhmmss() string {
320 return fmt.Sprintf("%2d:%02d:%02d", t.Hour(), t.Minute(), t.Second())
323 // record the latest screen size
324 func (state *State) ScreenSetSize(width, height int) {
325 state.screen.SetSize(width, height)
328 // clean up screen and disconnect database
329 func (state *State) Cleanup() {
331 if state.dbh != nil {
332 _ = state.dbh.Close()