1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  * Android BPF library - public API
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #pragma once
19 
20 #include <inttypes.h>
21 #include <log/log.h>
22 #include <time.h>
23 #include <sstream>
24 #include <string>
25 
26 /**
27  * An object that can track changes of some value over time, taking into account an additional
28  * dimension: the object's state.  As the tracked value changes, the deltas are distributed
29  * among the object states in accordance with the time spent in those states.
30  */
31 namespace android {
32 namespace battery {
33 
34 #define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000
35 
36 typedef uint16_t state_t;
37 
38 template <class T>
39 class MultiStateCounter {
40     uint16_t stateCount;
41     state_t currentState;
42     time_t lastStateChangeTimestamp;
43     T emptyValue;
44     T lastValue;
45     time_t lastUpdateTimestamp;
46     T deltaValue;
47     bool isEnabled;
48 
49     struct State {
50         time_t timeInStateSinceUpdate;
51         T counter;
52     };
53 
54     State* states;
55 
56 public:
57     MultiStateCounter(uint16_t stateCount, const T& emptyValue);
58 
59     virtual ~MultiStateCounter();
60 
61     void setEnabled(bool enabled, time_t timestamp);
62 
63     void setState(state_t state, time_t timestamp);
64 
65     /**
66      * Copies the current state and accumulated times-in-state from the source. Resets
67      * the accumulated value.
68      */
69     void copyStatesFrom(const MultiStateCounter<T>& source);
70 
71     void setValue(state_t state, const T& value);
72 
73     /**
74      * Updates the value by distributing the delta from the previously set value
75      * among states according to their respective time-in-state.
76      * Returns the delta from the previously set value.
77      */
78     const T& updateValue(const T& value, time_t timestamp);
79 
80     /**
81      * Updates the value by distributing the specified increment among states according
82      * to their respective time-in-state.
83      */
84     void incrementValue(const T& increment, time_t timestamp);
85 
86     /**
87      * Adds the specified increment to the value for the current state, without affecting
88      * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
89      * the increment is given to the current state.
90      */
91     void addValue(const T& increment);
92 
93     void reset();
94 
95     uint16_t getStateCount();
96 
97     const T& getCount(state_t state);
98 
99     std::string toString();
100 
101 private:
102     /**
103      * Subtracts previousValue from newValue and returns the result in outValue.
104      * Returns true iff the combination of previousValue and newValue is valid
105      * (newValue >= prevValue)
106      */
107     bool delta(const T& previousValue, const T& newValue, T* outValue) const;
108 
109     /**
110      * Adds value2 to value1 and stores the result in value1.  Denominator is
111      * guaranteed to be non-zero.
112      */
113     void add(T* value1, const T& value2, const uint64_t numerator,
114              const uint64_t denominator) const;
115 
116     std::string valueToString(const T& value) const;
117 };
118 
119 // ---------------------- MultiStateCounter Implementation -------------------------
120 // Since MultiStateCounter is a template, the implementation must be inlined.
121 
122 template <class T>
MultiStateCounter(uint16_t stateCount,const T & emptyValue)123 MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
124       : stateCount(stateCount),
125         currentState(0),
126         lastStateChangeTimestamp(-1),
127         emptyValue(emptyValue),
128         lastValue(emptyValue),
129         lastUpdateTimestamp(-1),
130         deltaValue(emptyValue),
131         isEnabled(true) {
132     states = new State[stateCount];
133     for (int i = 0; i < stateCount; i++) {
134         states[i].timeInStateSinceUpdate = 0;
135         states[i].counter = emptyValue;
136     }
137 }
138 
139 template <class T>
~MultiStateCounter()140 MultiStateCounter<T>::~MultiStateCounter() {
141     delete[] states;
142 };
143 
144 template <class T>
setEnabled(bool enabled,time_t timestamp)145 void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
146     if (enabled == isEnabled) {
147         return;
148     }
149 
150     if (isEnabled) {
151         // Confirm the current state for the side-effect of updating the time-in-state
152         // counter for the current state.
153         setState(currentState, timestamp);
154         isEnabled = false;
155     } else {
156         // If the counter is being enabled with an out-of-order timestamp, just push back
157         // the timestamp to avoid having the situation where
158         // timeInStateSinceUpdate > timeSinceUpdate
159         if (timestamp < lastUpdateTimestamp) {
160             timestamp = lastUpdateTimestamp;
161         }
162 
163         if (lastStateChangeTimestamp >= 0) {
164             lastStateChangeTimestamp = timestamp;
165         }
166         isEnabled = true;
167     }
168 }
169 
170 template <class T>
setState(state_t state,time_t timestamp)171 void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
172     if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
173         // If the update arrived out-of-order, just push back the timestamp to
174         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
175         if (timestamp < lastUpdateTimestamp) {
176             timestamp = lastUpdateTimestamp;
177         }
178 
179         if (timestamp >= lastStateChangeTimestamp) {
180             states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
181         } else {
182             if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
183                 ALOGE("setState is called with an earlier timestamp: %lu, "
184                       "previous timestamp: %lu\n",
185                       (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
186             }
187 
188             // The accumulated durations have become unreliable. For example, if the timestamp
189             // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
190             // we would get 4000, which is greater than (last - first). This could lead to
191             // counts exceeding 100%.
192             for (int i = 0; i < stateCount; i++) {
193                 states[i].timeInStateSinceUpdate = 0;
194             }
195         }
196     }
197     currentState = state;
198     lastStateChangeTimestamp = timestamp;
199 }
200 
201 template <class T>
copyStatesFrom(const MultiStateCounter<T> & source)202 void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
203     if (stateCount != source.stateCount) {
204         ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
205         return;
206     }
207 
208     currentState = source.currentState;
209     for (int i = 0; i < stateCount; i++) {
210         states[i].timeInStateSinceUpdate = source.states[i].timeInStateSinceUpdate;
211         states[i].counter = emptyValue;
212     }
213     lastStateChangeTimestamp = source.lastStateChangeTimestamp;
214     lastUpdateTimestamp = source.lastUpdateTimestamp;
215 }
216 
217 template <class T>
setValue(state_t state,const T & value)218 void MultiStateCounter<T>::setValue(state_t state, const T& value) {
219     states[state].counter = value;
220 }
221 
222 template <class T>
updateValue(const T & value,time_t timestamp)223 const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
224     T* returnValue = &emptyValue;
225 
226     // If the counter is disabled, we ignore the update, except when the counter got disabled after
227     // the previous update, in which case we still need to pick up the residual delta.
228     if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
229         // If the update arrived out of order, just push back the timestamp to
230         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
231         if (timestamp < lastStateChangeTimestamp) {
232             timestamp = lastStateChangeTimestamp;
233         }
234 
235         // Confirm the current state for the side-effect of updating the time-in-state
236         // counter for the current state.
237         setState(currentState, timestamp);
238 
239         if (lastUpdateTimestamp >= 0) {
240             if (timestamp > lastUpdateTimestamp) {
241                 if (delta(lastValue, value, &deltaValue)) {
242                     returnValue = &deltaValue;
243                     time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
244                     for (int i = 0; i < stateCount; i++) {
245                         time_t timeInState = states[i].timeInStateSinceUpdate;
246                         if (timeInState) {
247                             add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
248                             states[i].timeInStateSinceUpdate = 0;
249                         }
250                     }
251                 } else {
252                     std::stringstream str;
253                     str << "updateValue is called with a value " << valueToString(value)
254                         << ", which is lower than the previous value " << valueToString(lastValue)
255                         << "\n";
256                     ALOGE("%s", str.str().c_str());
257 
258                     for (int i = 0; i < stateCount; i++) {
259                         states[i].timeInStateSinceUpdate = 0;
260                     }
261                 }
262             } else if (timestamp < lastUpdateTimestamp) {
263                 if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
264                     ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
265                           (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
266                 }
267 
268                 for (int i = 0; i < stateCount; i++) {
269                     states[i].timeInStateSinceUpdate = 0;
270                 }
271             }
272         }
273     }
274     lastValue = value;
275     lastUpdateTimestamp = timestamp;
276     return *returnValue;
277 }
278 
279 template <class T>
incrementValue(const T & increment,time_t timestamp)280 void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
281     T newValue = lastValue;
282     add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
283     updateValue(newValue, timestamp);
284 }
285 
286 template <class T>
addValue(const T & value)287 void MultiStateCounter<T>::addValue(const T& value) {
288     if (!isEnabled) {
289         return;
290     }
291     add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
292 }
293 
294 template <class T>
reset()295 void MultiStateCounter<T>::reset() {
296     lastStateChangeTimestamp = -1;
297     lastUpdateTimestamp = -1;
298     for (int i = 0; i < stateCount; i++) {
299         states[i].timeInStateSinceUpdate = 0;
300         states[i].counter = emptyValue;
301     }
302 }
303 
304 template <class T>
getStateCount()305 uint16_t MultiStateCounter<T>::getStateCount() {
306     return stateCount;
307 }
308 
309 template <class T>
getCount(state_t state)310 const T& MultiStateCounter<T>::getCount(state_t state) {
311     return states[state].counter;
312 }
313 
314 template <class T>
toString()315 std::string MultiStateCounter<T>::toString() {
316     std::stringstream str;
317     str << "[";
318     for (int i = 0; i < stateCount; i++) {
319         if (i != 0) {
320             str << ", ";
321         }
322         str << i << ": " << valueToString(states[i].counter);
323         if (states[i].timeInStateSinceUpdate > 0) {
324             str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
325         }
326     }
327     str << "]";
328     if (lastUpdateTimestamp >= 0) {
329         str << " updated: " << lastUpdateTimestamp;
330     }
331     if (lastStateChangeTimestamp >= 0) {
332         str << " currentState: " << currentState;
333         if (lastStateChangeTimestamp > lastUpdateTimestamp) {
334             str << " stateChanged: " << lastStateChangeTimestamp;
335         }
336     } else {
337         str << " currentState: none";
338     }
339     if (!isEnabled) {
340         str << " disabled";
341     }
342     return str.str();
343 }
344 
345 } // namespace battery
346 } // namespace android
347