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 tiwsbt "github.com/sjmudd/pstop/p_s/table_io_waits_summary_by_table"
17 tlwsbt "github.com/sjmudd/pstop/p_s/table_lock_waits_summary_by_table"
18 "github.com/sjmudd/pstop/screen"
19 "github.com/sjmudd/pstop/version"
22 // what information to show
38 fsbi ps_table.Tabler // ufsbi.File_summary_by_instance
39 tiwsbt tiwsbt.Table_io_waits_summary_by_table
40 tlwsbt ps_table.Tabler // tlwsbt.Table_lock_waits_summary_by_table
42 screen screen.TermboxScreen
45 want_relative_stats bool
48 func (state *State) Setup(dbh *sql.DB) {
51 state.screen.Initialise()
53 _, variables := lib.SelectAllGlobalVariablesByVariableName(state.dbh)
54 // setup to their initial types/values
55 state.fsbi = fsbi.NewFileSummaryByInstance(variables)
56 state.tlwsbt = new(tlwsbt.Table_lock_waits_summary_by_table)
58 state.want_relative_stats = true // we show info from the point we start collecting data
59 state.fsbi.SetWantRelativeStats(state.want_relative_stats)
61 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
63 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
65 state.users.SetWantRelativeStats(state.want_relative_stats) // ignored
66 state.users.SetNow() // ignored
68 state.ResetDBStatistics()
71 state.show = showLatency
72 state.tiwsbt.SetWantsLatency(true)
74 // get short name (to save space)
75 _, hostname := lib.SelectGlobalVariableByVariableName(state.dbh, "HOSTNAME")
76 if index := strings.Index(hostname, "."); index >= 0 {
77 hostname = hostname[0:index]
79 _, mysql_version := lib.SelectGlobalVariableByVariableName(state.dbh, "VERSION")
80 _, datadir := lib.SelectGlobalVariableByVariableName(state.dbh, "DATADIR")
81 state.SetHostname(hostname)
82 state.SetMySQLVersion(mysql_version)
83 state.SetDatadir(datadir)
86 // do a fresh collection of data and then update the initial values based on that.
87 func (state *State) ResetDBStatistics() {
89 state.SyncReferenceValues()
92 func (state *State) SyncReferenceValues() {
94 state.fsbi.SyncReferenceValues()
95 state.tlwsbt.SyncReferenceValues()
96 state.tiwsbt.SyncReferenceValues()
97 lib.Logger.Println("state.SyncReferenceValues() took", time.Duration(time.Since(start)).String())
100 // collect all initial values on startup / reset
101 func (state *State) CollectAll() {
102 state.fsbi.Collect(state.dbh)
103 state.tlwsbt.Collect(state.dbh)
104 state.tiwsbt.Collect(state.dbh)
107 // Only collect the data we are looking at.
108 func (state *State) Collect() {
112 case showLatency, showOps:
113 state.tiwsbt.Collect(state.dbh)
115 state.fsbi.Collect(state.dbh)
117 state.tlwsbt.Collect(state.dbh)
119 state.users.Collect(state.dbh)
121 lib.Logger.Println("state.Collect() took", time.Duration(time.Since(start)).String())
124 func (state State) MySQLVersion() string {
125 return state.mysql_version
128 func (state State) Datadir() string {
132 func (state *State) SetHelp(newHelp bool) {
139 func (state *State) SetDatadir(datadir string) {
140 state.datadir = datadir
143 func (state *State) SetMySQLVersion(mysql_version string) {
144 state.mysql_version = mysql_version
147 func (state *State) SetHostname(hostname string) {
148 state.hostname = hostname
151 func (state State) Help() bool {
155 // display the output according to the mode we are in
156 func (state *State) Display() {
158 state.screen.DisplayHelp()
160 state.displayHeading()
162 case showLatency, showOps:
163 state.displayOpsOrLatency()
174 // change to the next display mode
175 func (state *State) DisplayNext() {
176 if state.show == showUsers {
177 state.show = showLatency
181 // this needs to be done more cleanly
182 if state.show == showLatency {
183 state.tiwsbt.SetWantsLatency(true)
185 if state.show == showOps {
186 state.tiwsbt.SetWantsLatency(false)
192 func (state State) displayHeading() {
194 state.displayDescription()
197 func (state State) displayLine0() {
198 _, uptime := lib.SelectGlobalStatusByVariableName(state.dbh, "UPTIME")
199 top_line := lib.MyName() + " " + version.Version() + " - " + now_hhmmss() + " " + state.hostname + " / " + state.mysql_version + ", up " + fmt.Sprintf("%-16s", lib.Uptime(uptime))
200 if state.want_relative_stats {
203 var initial time.Time
206 case showLatency, showOps:
207 initial = state.tiwsbt.Last()
209 initial = state.fsbi.Last()
211 initial = state.tlwsbt.Last()
213 initial = state.users.Last()
215 // should not get here !
218 d := now.Sub(initial)
220 top_line = top_line + " [REL] " + fmt.Sprintf("%.0f seconds", d.Seconds())
222 top_line = top_line + " [ABS] "
224 state.screen.PrintAt(0, 0, top_line)
227 func (state State) displayDescription() {
228 description := "UNKNOWN"
231 case showLatency, showOps:
232 description = state.tiwsbt.Description()
234 description = state.fsbi.Description()
236 description = state.tlwsbt.Description()
238 description = state.users.Description()
241 state.screen.PrintAt(0, 1, description)
244 func (state *State) displayOpsOrLatency() {
245 state.screen.PrintAt(0, 2, state.tiwsbt.Headings())
247 max_rows := state.screen.Height() - 3
248 row_content := state.tiwsbt.RowContent(max_rows)
251 for k := range row_content {
253 state.screen.PrintAt(0, y, row_content[k])
255 // print out empty rows
256 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
258 if y < state.screen.Height()-1 {
259 state.screen.PrintAt(0, y, state.tiwsbt.EmptyRowContent())
263 // print out the totals at the bottom
264 state.screen.PrintAt(0, state.screen.Height()-1, state.tiwsbt.TotalRowContent())
267 // show actual I/O latency values
268 func (state State) displayIO() {
269 state.screen.PrintAt(0, 2, state.fsbi.Headings())
271 // print out the data
272 max_rows := state.screen.Height() - 3
273 row_content := state.fsbi.RowContent(max_rows)
276 for k := range row_content {
278 state.screen.PrintAt(0, y, row_content[k])
280 // print out empty rows
281 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
283 if y < state.screen.Height()-1 {
284 state.screen.PrintAt(0, y, state.fsbi.EmptyRowContent())
288 // print out the totals at the bottom
289 state.screen.PrintAt(0, state.screen.Height()-1, state.fsbi.TotalRowContent())
292 func (state *State) displayLocks() {
293 state.screen.PrintAt(0, 2, state.tlwsbt.Headings())
295 // print out the data
296 max_rows := state.screen.Height() - 3
297 row_content := state.tlwsbt.RowContent(max_rows)
300 for k := range row_content {
302 state.screen.PrintAt(0, y, row_content[k])
304 // print out empty rows
305 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
307 if y < state.screen.Height()-1 {
308 state.screen.PrintAt(0, y, state.tlwsbt.EmptyRowContent())
312 // print out the totals at the bottom
313 state.screen.PrintAt(0, state.screen.Height()-1, state.tlwsbt.TotalRowContent())
316 func (state *State) displayUsers() {
317 state.screen.PrintAt(0, 2, state.users.Headings())
319 // print out the data
320 max_rows := state.screen.Height() - 3
321 row_content := state.users.RowContent(max_rows)
324 for k := range row_content {
326 state.screen.PrintAt(0, y, row_content[k])
328 // print out empty rows
329 for k := len(row_content); k < (state.screen.Height() - 3); k++ {
331 if y < state.screen.Height()-1 {
332 state.screen.PrintAt(0, y, state.users.EmptyRowContent())
336 // print out the totals at the bottom
337 state.screen.PrintAt(0, state.screen.Height()-1, state.users.TotalRowContent())
340 // do we want to show all p_s data?
341 func (state State) WantRelativeStats() bool {
342 return state.want_relative_stats
345 // set if we want data from when we started/reset stats.
346 func (state *State) SetWantRelativeStats(want_relative_stats bool) {
347 state.want_relative_stats = want_relative_stats
349 state.fsbi.SetWantRelativeStats(want_relative_stats)
350 state.tlwsbt.SetWantRelativeStats(state.want_relative_stats)
351 state.tiwsbt.SetWantRelativeStats(state.want_relative_stats)
354 // if there's a better way of doing this do it better ...
355 func now_hhmmss() string {
357 return fmt.Sprintf("%2d:%02d:%02d", t.Hour(), t.Minute(), t.Second())
360 // record the latest screen size
361 func (state *State) ScreenSetSize(width, height int) {
362 state.screen.SetSize(width, height)
365 // clean up screen and disconnect database
366 func (state *State) Cleanup() {
368 if state.dbh != nil {
369 _ = state.dbh.Close()