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 fsbi "github.com/sjmudd/pstop/p_s/file_summary_by_instance"
15 "github.com/sjmudd/pstop/p_s/ps_table"
16 "github.com/sjmudd/pstop/wait_info"
17 tiwsbt "github.com/sjmudd/pstop/p_s/table_io_waits_summary_by_table"
18 tlwsbt "github.com/sjmudd/pstop/p_s/table_lock_waits_summary_by_table"
19 "github.com/sjmudd/pstop/screen"
20 "github.com/sjmudd/pstop/version"
23 // what information to show
40 fsbi ps_table.Tabler // ufsbi.File_summary_by_instance
41 tiwsbt tiwsbt.Table_io_waits_summary_by_table
42 tlwsbt ps_table.Tabler // tlwsbt.Table_lock_waits_summary_by_table
44 screen screen.TermboxScreen
47 want_relative_stats bool
48 wait_info.WaitInfo // embedded
51 func (state *State) Setup(dbh *sql.DB) {
53 state.finished = false
55 state.screen.Initialise()
57 _, variables := lib.SelectAllGlobalVariablesByVariableName(state.dbh)
58 // setup to their initial types/values
59 state.fsbi = fsbi.NewFileSummaryByInstance(variables)
60 state.tlwsbt = new(tlwsbt.Table_lock_waits_summary_by_table)
62 state.want_relative_stats = true // we show info from the point we start collecting data
63 state.fsbi.SetWantRelativeStats(state.want_relative_stats)
65 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
67 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
69 state.users.SetWantRelativeStats(state.want_relative_stats) // ignored
70 state.users.SetNow() // ignored
72 state.ResetDBStatistics()
75 state.show = showLatency
76 state.tiwsbt.SetWantsLatency(true)
78 // get short name (to save space)
79 _, hostname := lib.SelectGlobalVariableByVariableName(state.dbh, "HOSTNAME")
80 if index := strings.Index(hostname, "."); index >= 0 {
81 hostname = hostname[0:index]
83 _, mysql_version := lib.SelectGlobalVariableByVariableName(state.dbh, "VERSION")
84 _, datadir := lib.SelectGlobalVariableByVariableName(state.dbh, "DATADIR")
85 state.SetHostname(hostname)
86 state.SetMySQLVersion(mysql_version)
87 state.SetDatadir(datadir)
91 func (state State) Finished() bool {
95 // indicate we have finished
96 func (state *State) SetFinished() {
100 // do a fresh collection of data and then update the initial values based on that.
101 func (state *State) ResetDBStatistics() {
103 state.SyncReferenceValues()
106 func (state *State) SyncReferenceValues() {
108 state.fsbi.SyncReferenceValues()
109 state.tlwsbt.SyncReferenceValues()
110 state.tiwsbt.SyncReferenceValues()
111 lib.Logger.Println("state.SyncReferenceValues() took", time.Duration(time.Since(start)).String())
114 // collect all initial values on startup / reset
115 func (state *State) CollectAll() {
116 state.fsbi.Collect(state.dbh)
117 state.tlwsbt.Collect(state.dbh)
118 state.tiwsbt.Collect(state.dbh)
121 // Only collect the data we are looking at.
122 func (state *State) Collect() {
126 case showLatency, showOps:
127 state.tiwsbt.Collect(state.dbh)
129 state.fsbi.Collect(state.dbh)
131 state.tlwsbt.Collect(state.dbh)
133 state.users.Collect(state.dbh)
135 lib.Logger.Println("state.Collect() took", time.Duration(time.Since(start)).String())
138 func (state State) MySQLVersion() string {
139 return state.mysql_version
142 func (state State) Datadir() string {
146 func (state *State) SetHelp(newHelp bool) {
153 func (state *State) SetDatadir(datadir string) {
154 state.datadir = datadir
157 func (state *State) SetMySQLVersion(mysql_version string) {
158 state.mysql_version = mysql_version
161 func (state *State) SetHostname(hostname string) {
162 state.hostname = hostname
165 func (state State) Help() bool {
169 // states go: showLatency -> showOps -> showIO -> showLocks -> showUsers
171 // display the output according to the mode we are in
172 func (state *State) Display() {
174 state.screen.DisplayHelp()
176 state.displayHeading()
178 case showLatency, showOps:
179 state.displayOpsOrLatency()
190 // fix_latency_setting() ensures the SetWantsLatency() value is
191 // correct. This needs to be done more cleanly.
192 func (state *State) fix_latency_setting() {
193 if state.show == showLatency {
194 state.tiwsbt.SetWantsLatency(true)
196 if state.show == showOps {
197 state.tiwsbt.SetWantsLatency(false)
201 // change to the previous display mode
202 func (state *State) DisplayPrevious() {
203 if state.show == showLatency {
204 state.show = showUsers
208 state.fix_latency_setting()
213 // change to the next display mode
214 func (state *State) DisplayNext() {
215 if state.show == showUsers {
216 state.show = showLatency
220 state.fix_latency_setting()
225 func (state State) displayHeading() {
227 state.displayDescription()
230 func (state State) displayLine0() {
231 _, uptime := lib.SelectGlobalStatusByVariableName(state.dbh, "UPTIME")
232 top_line := lib.MyName() + " " + version.Version() + " - " + now_hhmmss() + " " + state.hostname + " / " + state.mysql_version + ", up " + fmt.Sprintf("%-16s", lib.Uptime(uptime))
233 if state.want_relative_stats {
236 var initial time.Time
239 case showLatency, showOps:
240 initial = state.tiwsbt.Last()
242 initial = state.fsbi.Last()
244 initial = state.tlwsbt.Last()
246 initial = state.users.Last()
248 // should not get here !
251 d := now.Sub(initial)
253 top_line = top_line + " [REL] " + fmt.Sprintf("%.0f seconds", d.Seconds())
255 top_line = top_line + " [ABS] "
257 state.screen.PrintAt(0, 0, top_line)
260 func (state State) displayDescription() {
261 description := "UNKNOWN"
264 case showLatency, showOps:
265 description = state.tiwsbt.Description()
267 description = state.fsbi.Description()
269 description = state.tlwsbt.Description()
271 description = state.users.Description()
274 state.screen.PrintAt(0, 1, description)
277 func (state *State) displayOpsOrLatency() {
278 state.screen.BoldPrintAt(0, 2, state.tiwsbt.Headings())
280 max_rows := state.screen.Height() - 3
281 row_content := state.tiwsbt.RowContent(max_rows)
284 for k := range row_content {
286 state.screen.PrintAt(0, y, row_content[k])
288 // print out empty rows
289 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
291 if y < state.screen.Height()-1 {
292 state.screen.PrintAt(0, y, state.tiwsbt.EmptyRowContent())
296 // print out the totals at the bottom
297 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.tiwsbt.TotalRowContent())
300 // show actual I/O latency values
301 func (state State) displayIO() {
302 state.screen.BoldPrintAt(0, 2, state.fsbi.Headings())
304 // print out the data
305 max_rows := state.screen.Height() - 3
306 row_content := state.fsbi.RowContent(max_rows)
309 for k := range row_content {
311 state.screen.PrintAt(0, y, row_content[k])
313 // print out empty rows
314 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
316 if y < state.screen.Height()-1 {
317 state.screen.PrintAt(0, y, state.fsbi.EmptyRowContent())
321 // print out the totals at the bottom
322 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.fsbi.TotalRowContent())
325 func (state *State) displayLocks() {
326 state.screen.BoldPrintAt(0, 2, state.tlwsbt.Headings())
328 // print out the data
329 max_rows := state.screen.Height() - 3
330 row_content := state.tlwsbt.RowContent(max_rows)
333 for k := range row_content {
335 state.screen.PrintAt(0, y, row_content[k])
337 // print out empty rows
338 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
340 if y < state.screen.Height()-1 {
341 state.screen.PrintAt(0, y, state.tlwsbt.EmptyRowContent())
345 // print out the totals at the bottom
346 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.tlwsbt.TotalRowContent())
349 func (state *State) displayUsers() {
350 state.screen.BoldPrintAt(0, 2, state.users.Headings())
352 // print out the data
353 max_rows := state.screen.Height() - 3
354 row_content := state.users.RowContent(max_rows)
357 for k := range row_content {
359 state.screen.PrintAt(0, y, row_content[k])
361 // print out empty rows
362 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
364 if y < state.screen.Height()-1 {
365 state.screen.PrintAt(0, y, state.users.EmptyRowContent())
369 // print out the totals at the bottom
370 state.screen.BoldPrintAt(0, state.screen.Height()-1, state.users.TotalRowContent())
373 // do we want to show all p_s data?
374 func (state State) WantRelativeStats() bool {
375 return state.want_relative_stats
378 // set if we want data from when we started/reset stats.
379 func (state *State) SetWantRelativeStats(want_relative_stats bool) {
380 state.want_relative_stats = want_relative_stats
382 state.fsbi.SetWantRelativeStats(want_relative_stats)
383 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
384 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
387 // if there's a better way of doing this do it better ...
388 func now_hhmmss() string {
390 return fmt.Sprintf("%2d:%02d:%02d", t.Hour(), t.Minute(), t.Second())
393 // record the latest screen size
394 func (state *State) ScreenSetSize(width, height int) {
395 state.screen.SetSize(width, height)
398 // clean up screen and disconnect database
399 func (state *State) Cleanup() {
401 if state.dbh != nil {
402 _ = state.dbh.Close()