Source file src/cmd/vendor/github.com/google/pprof/internal/measurement/measurement.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package measurement
17
18 import (
19 "fmt"
20 "math"
21 "strings"
22 "time"
23
24 "github.com/google/pprof/profile"
25 )
26
27
28
29
30 func ScaleProfiles(profiles []*profile.Profile) error {
31 if len(profiles) == 0 {
32 return nil
33 }
34 periodTypes := make([]*profile.ValueType, 0, len(profiles))
35 for _, p := range profiles {
36 if p.PeriodType != nil {
37 periodTypes = append(periodTypes, p.PeriodType)
38 }
39 }
40 periodType, err := CommonValueType(periodTypes)
41 if err != nil {
42 return fmt.Errorf("period type: %v", err)
43 }
44
45
46 numSampleTypes := len(profiles[0].SampleType)
47 for _, p := range profiles[1:] {
48 if numSampleTypes != len(p.SampleType) {
49 return fmt.Errorf("inconsistent samples type count: %d != %d", numSampleTypes, len(p.SampleType))
50 }
51 }
52 sampleType := make([]*profile.ValueType, numSampleTypes)
53 for i := 0; i < numSampleTypes; i++ {
54 sampleTypes := make([]*profile.ValueType, len(profiles))
55 for j, p := range profiles {
56 sampleTypes[j] = p.SampleType[i]
57 }
58 sampleType[i], err = CommonValueType(sampleTypes)
59 if err != nil {
60 return fmt.Errorf("sample types: %v", err)
61 }
62 }
63
64 for _, p := range profiles {
65 if p.PeriodType != nil && periodType != nil {
66 period, _ := Scale(p.Period, p.PeriodType.Unit, periodType.Unit)
67 p.Period, p.PeriodType.Unit = int64(period), periodType.Unit
68 }
69 ratios := make([]float64, len(p.SampleType))
70 for i, st := range p.SampleType {
71 if sampleType[i] == nil {
72 ratios[i] = 1
73 continue
74 }
75 ratios[i], _ = Scale(1, st.Unit, sampleType[i].Unit)
76 p.SampleType[i].Unit = sampleType[i].Unit
77 }
78 if err := p.ScaleN(ratios); err != nil {
79 return fmt.Errorf("scale: %v", err)
80 }
81 }
82 return nil
83 }
84
85
86
87 func CommonValueType(ts []*profile.ValueType) (*profile.ValueType, error) {
88 if len(ts) <= 1 {
89 return nil, nil
90 }
91 minType := ts[0]
92 for _, t := range ts[1:] {
93 if !compatibleValueTypes(minType, t) {
94 return nil, fmt.Errorf("incompatible types: %v %v", *minType, *t)
95 }
96 if ratio, _ := Scale(1, t.Unit, minType.Unit); ratio < 1 {
97 minType = t
98 }
99 }
100 rcopy := *minType
101 return &rcopy, nil
102 }
103
104 func compatibleValueTypes(v1, v2 *profile.ValueType) bool {
105 if v1 == nil || v2 == nil {
106 return true
107 }
108
109 if t1, t2 := strings.TrimSuffix(v1.Type, "s"), strings.TrimSuffix(v2.Type, "s"); t1 != t2 {
110 return false
111 }
112
113 return v1.Unit == v2.Unit ||
114 (isTimeUnit(v1.Unit) && isTimeUnit(v2.Unit)) ||
115 (isMemoryUnit(v1.Unit) && isMemoryUnit(v2.Unit))
116 }
117
118
119
120
121 func Scale(value int64, fromUnit, toUnit string) (float64, string) {
122
123 if value < 0 && -value > 0 {
124 v, u := Scale(-value, fromUnit, toUnit)
125 return -v, u
126 }
127 if m, u, ok := memoryLabel(value, fromUnit, toUnit); ok {
128 return m, u
129 }
130 if t, u, ok := timeLabel(value, fromUnit, toUnit); ok {
131 return t, u
132 }
133
134 switch toUnit {
135 case "count", "sample", "unit", "minimum", "auto":
136 return float64(value), ""
137 default:
138 return float64(value), toUnit
139 }
140 }
141
142
143 func Label(value int64, unit string) string {
144 return ScaledLabel(value, unit, "auto")
145 }
146
147
148
149 func ScaledLabel(value int64, fromUnit, toUnit string) string {
150 v, u := Scale(value, fromUnit, toUnit)
151 sv := strings.TrimSuffix(fmt.Sprintf("%.2f", v), ".00")
152 if sv == "0" || sv == "-0" {
153 return "0"
154 }
155 return sv + u
156 }
157
158
159
160 func Percentage(value, total int64) string {
161 var ratio float64
162 if total != 0 {
163 ratio = math.Abs(float64(value)/float64(total)) * 100
164 }
165 switch {
166 case math.Abs(ratio) >= 99.95 && math.Abs(ratio) <= 100.05:
167 return " 100%"
168 case math.Abs(ratio) >= 1.0:
169 return fmt.Sprintf("%5.2f%%", ratio)
170 default:
171 return fmt.Sprintf("%5.2g%%", ratio)
172 }
173 }
174
175
176
177 func isMemoryUnit(unit string) bool {
178 switch strings.TrimSuffix(strings.ToLower(unit), "s") {
179 case "byte", "b", "kilobyte", "kb", "megabyte", "mb", "gigabyte", "gb":
180 return true
181 }
182 return false
183 }
184
185 func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
186 fromUnit = strings.TrimSuffix(strings.ToLower(fromUnit), "s")
187 toUnit = strings.TrimSuffix(strings.ToLower(toUnit), "s")
188
189 switch fromUnit {
190 case "byte", "b":
191 case "kb", "kbyte", "kilobyte":
192 value *= 1024
193 case "mb", "mbyte", "megabyte":
194 value *= 1024 * 1024
195 case "gb", "gbyte", "gigabyte":
196 value *= 1024 * 1024 * 1024
197 case "tb", "tbyte", "terabyte":
198 value *= 1024 * 1024 * 1024 * 1024
199 case "pb", "pbyte", "petabyte":
200 value *= 1024 * 1024 * 1024 * 1024 * 1024
201 default:
202 return 0, "", false
203 }
204
205 if toUnit == "minimum" || toUnit == "auto" {
206 switch {
207 case value < 1024:
208 toUnit = "b"
209 case value < 1024*1024:
210 toUnit = "kb"
211 case value < 1024*1024*1024:
212 toUnit = "mb"
213 case value < 1024*1024*1024*1024:
214 toUnit = "gb"
215 case value < 1024*1024*1024*1024*1024:
216 toUnit = "tb"
217 default:
218 toUnit = "pb"
219 }
220 }
221
222 var output float64
223 switch toUnit {
224 default:
225 output, toUnit = float64(value), "B"
226 case "kb", "kbyte", "kilobyte":
227 output, toUnit = float64(value)/1024, "kB"
228 case "mb", "mbyte", "megabyte":
229 output, toUnit = float64(value)/(1024*1024), "MB"
230 case "gb", "gbyte", "gigabyte":
231 output, toUnit = float64(value)/(1024*1024*1024), "GB"
232 case "tb", "tbyte", "terabyte":
233 output, toUnit = float64(value)/(1024*1024*1024*1024), "TB"
234 case "pb", "pbyte", "petabyte":
235 output, toUnit = float64(value)/(1024*1024*1024*1024*1024), "PB"
236 }
237 return output, toUnit, true
238 }
239
240
241 func isTimeUnit(unit string) bool {
242 unit = strings.ToLower(unit)
243 if len(unit) > 2 {
244 unit = strings.TrimSuffix(unit, "s")
245 }
246
247 switch unit {
248 case "nanosecond", "ns", "microsecond", "millisecond", "ms", "s", "second", "sec", "hr", "day", "week", "year":
249 return true
250 }
251 return false
252 }
253
254 func timeLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok bool) {
255 fromUnit = strings.ToLower(fromUnit)
256 if len(fromUnit) > 2 {
257 fromUnit = strings.TrimSuffix(fromUnit, "s")
258 }
259
260 toUnit = strings.ToLower(toUnit)
261 if len(toUnit) > 2 {
262 toUnit = strings.TrimSuffix(toUnit, "s")
263 }
264
265 var d time.Duration
266 switch fromUnit {
267 case "nanosecond", "ns":
268 d = time.Duration(value) * time.Nanosecond
269 case "microsecond":
270 d = time.Duration(value) * time.Microsecond
271 case "millisecond", "ms":
272 d = time.Duration(value) * time.Millisecond
273 case "second", "sec", "s":
274 d = time.Duration(value) * time.Second
275 case "cycle":
276 return float64(value), "", true
277 default:
278 return 0, "", false
279 }
280
281 if toUnit == "minimum" || toUnit == "auto" {
282 switch {
283 case d < 1*time.Microsecond:
284 toUnit = "ns"
285 case d < 1*time.Millisecond:
286 toUnit = "us"
287 case d < 1*time.Second:
288 toUnit = "ms"
289 case d < 1*time.Minute:
290 toUnit = "sec"
291 case d < 1*time.Hour:
292 toUnit = "min"
293 case d < 24*time.Hour:
294 toUnit = "hour"
295 case d < 15*24*time.Hour:
296 toUnit = "day"
297 case d < 120*24*time.Hour:
298 toUnit = "week"
299 default:
300 toUnit = "year"
301 }
302 }
303
304 var output float64
305 dd := float64(d)
306 switch toUnit {
307 case "ns", "nanosecond":
308 output, toUnit = dd/float64(time.Nanosecond), "ns"
309 case "us", "microsecond":
310 output, toUnit = dd/float64(time.Microsecond), "us"
311 case "ms", "millisecond":
312 output, toUnit = dd/float64(time.Millisecond), "ms"
313 case "min", "minute":
314 output, toUnit = dd/float64(time.Minute), "mins"
315 case "hour", "hr":
316 output, toUnit = dd/float64(time.Hour), "hrs"
317 case "day":
318 output, toUnit = dd/float64(24*time.Hour), "days"
319 case "week", "wk":
320 output, toUnit = dd/float64(7*24*time.Hour), "wks"
321 case "year", "yr":
322 output, toUnit = dd/float64(365*24*time.Hour), "yrs"
323 default:
324
325 output, toUnit = dd/float64(time.Second), "s"
326 }
327 return output, toUnit, true
328 }
329
View as plain text