Isolate display from collection of data
[pstop.git] / lib / common.go
1 // package lib - common routines for pstop
2 package lib
3
4 import (
5         "fmt"
6         _ "github.com/go-sql-driver/mysql"
7         _ "github.com/sjmudd/pstop/version"
8         "strconv"
9 )
10
11 const (
12         myname    = "pstop"
13         copyright = "Copyright (C) 2014 Simon J Mudd <sjmudd@pobox.com>"
14 )
15
16 // myround converts this floating value to the right width etc.
17 // There must be a function in Go to do this. Find it.
18 func myround(f float64, width, decimals int) string {
19         format := "%" + fmt.Sprintf("%d", width) + "." + fmt.Sprintf("%d", decimals) + "f"
20         return fmt.Sprintf(format, f)
21 }
22
23 // MyName returns the program's name.
24 func MyName() string {
25         return myname
26 }
27
28 // Copyright provides a copyright message for pstop
29 func Copyright() string {
30         return copyright
31 }
32
33 // sec_to_time() converts a number of hours, minutes and seconds into hh:mm:ss format.
34 // e.g. 7384 = 2h 3m 4s, 7200 + 180 + 4
35 func sec_to_time(d uint64) string {
36         hours := d / 3600                // integer value
37         minutes := (d - hours*3600) / 60 // integer value
38         seconds := d - hours*3600 - minutes*60
39
40         return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
41 }
42
43 // FormatTime is based on sys.format_time. It
44 // formats to 10 characters including space and suffix.
45 // All values have 2 decimal places. Zero is returned as
46 // an empty string.
47 func FormatTime(picoseconds uint64) string {
48         if picoseconds == 0 {
49                 return ""
50         }
51         if picoseconds >= 3600000000000000 {
52                 return myround(float64(picoseconds)/3600000000000000, 8, 2) + " h"
53         }
54         if picoseconds >= 60000000000000 {
55                 return sec_to_time(picoseconds / 1000000000000)
56         }
57         if picoseconds >= 1000000000000 {
58                 return myround(float64(picoseconds)/1000000000000, 8, 2) + " s"
59         }
60         if picoseconds >= 1000000000 {
61                 return myround(float64(picoseconds)/1000000000, 7, 2) + " ms"
62         }
63         if picoseconds >= 1000000 {
64                 return myround(float64(picoseconds)/1000000, 7, 2) + " us"
65         }
66         if picoseconds >= 1000 {
67                 return myround(float64(picoseconds)/1000, 7, 2) + " ns"
68         }
69         return strconv.Itoa(int(picoseconds)) + " ps"
70 }
71
72 // FormatPct() formats a floating point number as a percentage
73 // including the trailing % sign. Print the value as a %5.1f with
74 // a % suffix if there's a value.
75 // If the value is 0 print as 6 spaces.
76 // if the value is > 999.9 then show +++.+% to indicate an overflow.
77 func FormatPct(pct float64) string {
78         var s string
79         if pct < 0.0001 {
80                 s = "      "
81         } else if pct > 999.9 {
82                 s = "+++.+%" // too large to fit! (probably a bug as we don't expect this value to be > 100.00)
83         } else {
84                 s = fmt.Sprintf("%5.1f", 100.0*pct) + "%"
85         }
86
87         return s
88 }
89
90 // FormatAmount() convert numbers to k = 1024 , M = 1024 x 1024, G = 1024 x 1024 x 1024, P = 1024x1024x1024x1024.
91 // For values = 0 return an empty string.
92 // For values < 1000 show 6,2 decimal places.
93 // For values >= 1000 show 6,1 decimal place.
94 func FormatAmount(amount uint64) string {
95         var suffix string
96         var formatted string
97         var decimal_amount float64
98
99         if amount == 0 {
100                 return ""
101         }
102         if amount <= 1024 {
103                 return strconv.Itoa(int(amount))
104         }
105
106         if amount > (1024 * 1024 * 1024 * 1024) {
107                 suffix = "P"
108                 decimal_amount = float64(amount) / 1024 / 1024 / 1024 / 1024
109         } else if amount > (1024 * 1024 * 1024) {
110                 suffix = "G"
111                 decimal_amount = float64(amount) / 1024 / 1024 / 1024
112         } else if amount > (1024 * 1024) {
113                 suffix = "M"
114                 decimal_amount = float64(amount) / 1024 / 1024
115         } else if amount > 1024 {
116                 suffix = "k"
117                 decimal_amount = float64(amount) / 1024
118         }
119
120         if decimal_amount > 1000.0 {
121                 formatted = fmt.Sprintf("%6.1f %s", decimal_amount, suffix)
122         } else {
123                 formatted = fmt.Sprintf("%6.2f %s", decimal_amount, suffix)
124         }
125         return formatted
126 }
127
128 // MyDivide() divides a by b except if b is 0 in which case we return 0.
129 func MyDivide(a uint64, b uint64) float64 {
130         if b == 0 {
131                 return float64(0)
132         } else {
133                 return float64(a) / float64(b)
134         }
135 }
136
137 // Uptime() provides a  usable form of uptime.
138 // Note: this doesn't return a string of a fixed size!
139 // Minimum value: 1s.
140 // Maximum value: 100d 23h 59m 59s (sort of).
141 func Uptime(uptime int) string {
142         var result string
143
144         days := uptime / 24 / 60 / 60
145         hours := (uptime - days*86400) / 3600
146         minutes := (uptime - days*86400 - hours*3600) / 60
147         seconds := uptime - days*86400 - hours*3600 - minutes*60
148
149         result = strconv.Itoa(seconds) + "s"
150
151         if minutes > 0 {
152                 result = strconv.Itoa(minutes) + "m " + result
153         }
154         if hours > 0 {
155                 result = strconv.Itoa(hours) + "h " + result
156         }
157         if days > 0 {
158                 result = strconv.Itoa(days) + "d " + result
159         }
160
161         return result
162 }