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/i_s"
13 "github.com/sjmudd/pstop/lib"
14 ewsgben "github.com/sjmudd/pstop/p_s/events_waits_summary_global_by_event_name"
15 fsbi "github.com/sjmudd/pstop/p_s/file_summary_by_instance"
16 "github.com/sjmudd/pstop/p_s/ps_table"
17 "github.com/sjmudd/pstop/p_s/setup_instruments"
18 tiwsbt "github.com/sjmudd/pstop/p_s/table_io_waits_summary_by_table"
19 tlwsbt "github.com/sjmudd/pstop/p_s/table_lock_waits_summary_by_table"
20 "github.com/sjmudd/pstop/screen"
21 "github.com/sjmudd/pstop/version"
22 "github.com/sjmudd/pstop/wait_info"
25 // what information to show
43 fsbi ps_table.Tabler // ufsbi.File_summary_by_instance
44 tiwsbt tiwsbt.Table_io_waits_summary_by_table
45 tlwsbt ps_table.Tabler // tlwsbt.Table_lock_waits_summary_by_table
46 ewsgben ps_table.Tabler // ewsgben.Events_waits_summary_global_by_event_name
48 screen screen.TermboxScreen
51 want_relative_stats bool
52 wait_info.WaitInfo // embedded
53 setup_instruments setup_instruments.SetupInstruments
56 func (state *State) Setup(dbh *sql.DB) {
58 state.finished = false
60 state.screen.Initialise()
62 state.setup_instruments.EnableMutexMonitoring(dbh)
63 _, variables := lib.SelectAllGlobalVariablesByVariableName(state.dbh)
64 // setup to their initial types/values
65 state.fsbi = fsbi.NewFileSummaryByInstance(variables)
66 state.tlwsbt = new(tlwsbt.Table_lock_waits_summary_by_table)
67 state.ewsgben = new(ewsgben.Table_events_waits_summary_global_by_event_name)
69 state.want_relative_stats = true // we show info from the point we start collecting data
70 state.fsbi.SetWantRelativeStats(state.want_relative_stats)
72 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
74 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
76 state.users.SetWantRelativeStats(state.want_relative_stats) // ignored
77 state.users.SetNow() // ignored
78 state.ewsgben.SetWantRelativeStats(state.want_relative_stats) // ignored
79 state.ewsgben.SetNow() // ignored
81 state.ResetDBStatistics()
84 state.show = showLatency
85 state.tiwsbt.SetWantsLatency(true)
87 // get short name (to save space)
88 _, hostname := lib.SelectGlobalVariableByVariableName(state.dbh, "HOSTNAME")
89 if index := strings.Index(hostname, "."); index >= 0 {
90 hostname = hostname[0:index]
92 _, mysql_version := lib.SelectGlobalVariableByVariableName(state.dbh, "VERSION")
93 _, datadir := lib.SelectGlobalVariableByVariableName(state.dbh, "DATADIR")
94 state.SetHostname(hostname)
95 state.SetMySQLVersion(mysql_version)
96 state.SetDatadir(datadir)
100 func (state State) Finished() bool {
101 return state.finished
104 // indicate we have finished
105 func (state *State) SetFinished() {
106 state.finished = true
109 // do a fresh collection of data and then update the initial values based on that.
110 func (state *State) ResetDBStatistics() {
112 state.SyncReferenceValues()
115 func (state *State) SyncReferenceValues() {
117 state.fsbi.SyncReferenceValues()
118 state.tlwsbt.SyncReferenceValues()
119 state.tiwsbt.SyncReferenceValues()
120 lib.Logger.Println("state.SyncReferenceValues() took", time.Duration(time.Since(start)).String())
123 // collect all initial values on startup / reset
124 func (state *State) CollectAll() {
125 state.fsbi.Collect(state.dbh)
126 state.tlwsbt.Collect(state.dbh)
127 state.tiwsbt.Collect(state.dbh)
130 // Only collect the data we are looking at.
131 func (state *State) Collect() {
135 case showLatency, showOps:
136 state.tiwsbt.Collect(state.dbh)
138 state.fsbi.Collect(state.dbh)
140 state.tlwsbt.Collect(state.dbh)
142 state.users.Collect(state.dbh)
144 state.ewsgben.Collect(state.dbh)
146 lib.Logger.Println("state.Collect() took", time.Duration(time.Since(start)).String())
149 func (state State) MySQLVersion() string {
150 return state.mysql_version
153 func (state State) Datadir() string {
157 func (state *State) SetHelp(newHelp bool) {
164 func (state *State) SetDatadir(datadir string) {
165 state.datadir = datadir
168 func (state *State) SetMySQLVersion(mysql_version string) {
169 state.mysql_version = mysql_version
172 func (state *State) SetHostname(hostname string) {
173 state.hostname = hostname
176 func (state State) Help() bool {
180 // states go: showLatency -> showOps -> showIO -> showLocks -> showUsers -> showMutex
182 // display the output according to the mode we are in
183 func (state *State) Display() {
185 state.screen.DisplayHelp()
187 state.displayHeading()
189 case showLatency, showOps:
190 state.displayOpsOrLatency()
203 // fix_latency_setting() ensures the SetWantsLatency() value is
204 // correct. This needs to be done more cleanly.
205 func (state *State) fix_latency_setting() {
206 if state.show == showLatency {
207 state.tiwsbt.SetWantsLatency(true)
209 if state.show == showOps {
210 state.tiwsbt.SetWantsLatency(false)
214 // change to the previous display mode
215 func (state *State) DisplayPrevious() {
216 if state.show == showLatency {
217 state.show = showMutex
221 state.fix_latency_setting()
226 // change to the next display mode
227 func (state *State) DisplayNext() {
228 if state.show == showMutex {
229 state.show = showLatency
233 state.fix_latency_setting()
238 func (state State) displayHeading() {
240 state.displayDescription()
243 func (state State) displayLine0() {
244 _, uptime := lib.SelectGlobalStatusByVariableName(state.dbh, "UPTIME")
245 top_line := lib.MyName() + " " + version.Version() + " - " + now_hhmmss() + " " + state.hostname + " / " + state.mysql_version + ", up " + fmt.Sprintf("%-16s", lib.Uptime(uptime))
246 if state.want_relative_stats {
249 var initial time.Time
252 case showLatency, showOps:
253 initial = state.tiwsbt.Last()
255 initial = state.fsbi.Last()
257 initial = state.tlwsbt.Last()
259 initial = state.users.Last()
261 initial = state.ewsgben.Last()
263 // should not get here !
266 d := now.Sub(initial)
268 top_line = top_line + " [REL] " + fmt.Sprintf("%.0f seconds", d.Seconds())
270 top_line = top_line + " [ABS] "
272 state.screen.PrintAt(0, 0, top_line)
275 func (state State) displayDescription() {
276 description := "UNKNOWN"
279 case showLatency, showOps:
280 description = state.tiwsbt.Description()
282 description = state.fsbi.Description()
284 description = state.tlwsbt.Description()
286 description = state.users.Description()
288 description = state.ewsgben.Description()
291 state.screen.PrintAt(0, 1, description)
294 func (state *State) displayOpsOrLatency() {
295 state.screen.BoldPrintAt(0, 2, state.tiwsbt.Headings())
297 max_rows := state.screen.Height() - 3
298 row_content := state.tiwsbt.RowContent(max_rows)
301 for k := range row_content {
303 state.screen.PrintAt(0, y, row_content[k])
305 // print out empty rows
306 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
308 if y < state.screen.Height()-1 {
309 state.screen.PrintAt(0, y, state.tiwsbt.EmptyRowContent())
313 // print out the totals at the bottom
314 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.tiwsbt.TotalRowContent())
317 // show actual I/O latency values
318 func (state State) displayIO() {
319 state.screen.BoldPrintAt(0, 2, state.fsbi.Headings())
321 // print out the data
322 max_rows := state.screen.Height() - 3
323 row_content := state.fsbi.RowContent(max_rows)
326 for k := range row_content {
328 state.screen.PrintAt(0, y, row_content[k])
330 // print out empty rows
331 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
333 if y < state.screen.Height()-1 {
334 state.screen.PrintAt(0, y, state.fsbi.EmptyRowContent())
338 // print out the totals at the bottom
339 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.fsbi.TotalRowContent())
342 func (state *State) displayLocks() {
343 state.screen.BoldPrintAt(0, 2, state.tlwsbt.Headings())
345 // print out the data
346 max_rows := state.screen.Height() - 3
347 row_content := state.tlwsbt.RowContent(max_rows)
350 for k := range row_content {
352 state.screen.PrintAt(0, y, row_content[k])
354 // print out empty rows
355 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
357 if y < state.screen.Height()-1 {
358 state.screen.PrintAt(0, y, state.tlwsbt.EmptyRowContent())
362 // print out the totals at the bottom
363 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.tlwsbt.TotalRowContent())
366 func (state *State) displayUsers() {
367 state.screen.BoldPrintAt(0, 2, state.users.Headings())
369 // print out the data
370 max_rows := state.screen.Height() - 3
371 row_content := state.users.RowContent(max_rows)
374 for k := range row_content {
376 state.screen.PrintAt(0, y, row_content[k])
378 // print out empty rows
379 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
381 if y < state.screen.Height()-1 {
382 state.screen.PrintAt(0, y, state.users.EmptyRowContent())
386 // print out the totals at the bottom
387 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.users.TotalRowContent())
390 func (state *State) displayMutex() {
391 state.screen.BoldPrintAt(0, 2, state.ewsgben.Headings())
393 // print out the data
394 max_rows := state.screen.Height() - 3
395 row_content := state.ewsgben.RowContent(max_rows)
398 for k := range row_content {
400 state.screen.PrintAt(0, y, row_content[k])
402 // print out empty rows
403 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
405 if y < state.screen.Height()-1 {
406 state.screen.PrintAt(0, y, state.ewsgben.EmptyRowContent())
410 // print out the totals at the bottom
411 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.ewsgben.TotalRowContent())
414 // do we want to show all p_s data?
415 func (state State) WantRelativeStats() bool {
416 return state.want_relative_stats
419 // set if we want data from when we started/reset stats.
420 func (state *State) SetWantRelativeStats(want_relative_stats bool) {
421 state.want_relative_stats = want_relative_stats
423 state.fsbi.SetWantRelativeStats(want_relative_stats)
424 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
425 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
426 state.ewsgben.SetWantRelativeStats(state.want_relative_stats)
429 // if there's a better way of doing this do it better ...
430 func now_hhmmss() string {
432 return fmt.Sprintf("%2d:%02d:%02d", t.Hour(), t.Minute(), t.Second())
435 // record the latest screen size
436 func (state *State) ScreenSetSize(width, height int) {
437 state.screen.SetSize(width, height)
440 // clean up screen and disconnect database
441 func (state *State) Cleanup() {
443 if state.dbh != nil {
444 state.setup_instruments.Restore(state.dbh)
445 _ = state.dbh.Close()