net/sockstats: fix calculation of radio power usage

When splitting the radio monitor usage array, we were splitting at now %
3600 to get values into chronological order.  This caused the value for
the final second to be included at the beginning of the ordered slice
rather than the end.  If there was activity during that final second, an
extra five seconds of high power usage would get recorded in some cases.
This could result in a final calculation of greater than 100% usage.

This corrects that by splitting values at (now+1 % 3600).

This also simplifies the percentage calculation by always rounding
values down, which is sufficient for our usage.

Signed-off-by: Will Norris <will@tailscale.com>
pull/7928/head
Will Norris 2 years ago committed by Will Norris
parent 7f057d7489
commit 7c386ca6d2

@ -8,7 +8,6 @@ package sockstats
import ( import (
"context" "context"
"fmt" "fmt"
"math"
"net" "net"
"strings" "strings"
"sync" "sync"
@ -347,10 +346,12 @@ func (rm *radioMonitor) radioHighPercent() int64 {
} }
periodStart := now - periodLength // start of current reporting period periodStart := now - periodLength // start of current reporting period
// slices of radio usage, with values in chronological order // split into slices of radio usage, with values in chronological order.
// split at now+1 so that the current second is in the second slice.
split := (now + 1) % radioSampleSize
slices := [2][]int64{ slices := [2][]int64{
rm.usage[now%radioSampleSize:], rm.usage[split:],
rm.usage[:now%radioSampleSize], rm.usage[:split],
} }
var highPowerSec int64 // total seconds radio was in high power (active or idle) var highPowerSec int64 // total seconds radio was in high power (active or idle)
var c int // counter var c int // counter
@ -369,5 +370,8 @@ func (rm *radioMonitor) radioHighPercent() int64 {
} }
} }
return int64(math.Round(float64(highPowerSec) / float64(periodLength) * 100)) if highPowerSec == 0 {
return 0
}
return highPowerSec * 100 / periodLength
} }

@ -39,7 +39,7 @@ func TestRadioMonitor(t *testing.T) {
rm.active() rm.active()
tt.Add(10 * time.Second) tt.Add(10 * time.Second)
}, },
50, // radio on 5 seconds of every 10 seconds 50, // radio on 5 seconds of 10 seconds
}, },
{ {
"400 iterations: 2 sec active, 1 min idle", "400 iterations: 2 sec active, 1 min idle",
@ -49,11 +49,19 @@ func TestRadioMonitor(t *testing.T) {
rm.active() rm.active()
tt.Add(1 * time.Second) tt.Add(1 * time.Second)
rm.active() rm.active()
tt.Add(1 * time.Minute) tt.Add(59 * time.Second)
} }
}, },
10, // radio on 6 seconds of every minute 10, // radio on 6 seconds of every minute
}, },
{
"activity at end of time window",
func(tt *testTime, rm *radioMonitor) {
tt.Add(2 * time.Second)
rm.active()
},
50,
},
} }
for _, tt := range tests { for _, tt := range tests {

Loading…
Cancel
Save