• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 typedef uint16_t state_t;
35 
36 template <class T>
37 class MultiStateCounter {
38     uint16_t stateCount;
39     state_t currentState;
40     time_t lastStateChangeTimestamp;
41     T emptyValue;
42     T lastValue;
43     time_t lastUpdateTimestamp;
44     T deltaValue;
45     bool isEnabled;
46 
47     struct State {
48         time_t timeInStateSinceUpdate;
49         T counter;
50     };
51 
52     State* states;
53 
54 public:
55     MultiStateCounter(uint16_t stateCount, const T& emptyValue);
56 
57     virtual ~MultiStateCounter();
58 
59     void setEnabled(bool enabled, time_t timestamp);
60 
61     void setState(state_t state, time_t timestamp);
62 
63     void setValue(state_t state, const T& value);
64 
65     /**
66      * Updates the value by distributing the delta from the previously set value
67      * among states according to their respective time-in-state.
68      * Returns the delta from the previously set value.
69      */
70     const T& updateValue(const T& value, time_t timestamp);
71 
72     /**
73      * Updates the value by distributing the specified increment among states according
74      * to their respective time-in-state.
75      */
76     void incrementValue(const T& increment, time_t timestamp);
77 
78     /**
79      * Adds the specified increment to the value for the current state, without affecting
80      * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
81      * the increment is given to the current state.
82      */
83     void addValue(const T& increment);
84 
85     void reset();
86 
87     uint16_t getStateCount();
88 
89     const T& getCount(state_t state);
90 
91     std::string toString();
92 
93 private:
94     /**
95      * Subtracts previousValue from newValue and returns the result in outValue.
96      * Returns true iff the combination of previousValue and newValue is valid
97      * (newValue >= prevValue)
98      */
99     bool delta(const T& previousValue, const T& newValue, T* outValue) const;
100 
101     /**
102      * Adds value2 to value1 and stores the result in value1.  Denominator is
103      * guaranteed to be non-zero.
104      */
105     void add(T* value1, const T& value2, const uint64_t numerator,
106              const uint64_t denominator) const;
107 
108     std::string valueToString(const T& value) const;
109 };
110 
111 // ---------------------- MultiStateCounter Implementation -------------------------
112 // Since MultiStateCounter is a template, the implementation must be inlined.
113 
114 template <class T>
MultiStateCounter(uint16_t stateCount,const T & emptyValue)115 MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
116       : stateCount(stateCount),
117         currentState(0),
118         lastStateChangeTimestamp(-1),
119         emptyValue(emptyValue),
120         lastValue(emptyValue),
121         lastUpdateTimestamp(-1),
122         deltaValue(emptyValue),
123         isEnabled(true) {
124     states = new State[stateCount];
125     for (int i = 0; i < stateCount; i++) {
126         states[i].timeInStateSinceUpdate = 0;
127         states[i].counter = emptyValue;
128     }
129 }
130 
131 template <class T>
~MultiStateCounter()132 MultiStateCounter<T>::~MultiStateCounter() {
133     delete[] states;
134 };
135 
136 template <class T>
setEnabled(bool enabled,time_t timestamp)137 void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
138     if (enabled == isEnabled) {
139         return;
140     }
141 
142     if (isEnabled) {
143         // Confirm the current state for the side-effect of updating the time-in-state
144         // counter for the current state.
145         setState(currentState, timestamp);
146         isEnabled = false;
147     } else {
148         // If the counter is being enabled with an out-of-order timestamp, just push back
149         // the timestamp to avoid having the situation where
150         // timeInStateSinceUpdate > timeSinceUpdate
151         if (timestamp < lastUpdateTimestamp) {
152             timestamp = lastUpdateTimestamp;
153         }
154 
155         if (lastStateChangeTimestamp >= 0) {
156             lastStateChangeTimestamp = timestamp;
157         }
158         isEnabled = true;
159     }
160 }
161 
162 template <class T>
setState(state_t state,time_t timestamp)163 void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
164     if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
165         // If the update arrived out-of-order, just push back the timestamp to
166         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
167         if (timestamp < lastUpdateTimestamp) {
168             timestamp = lastUpdateTimestamp;
169         }
170 
171         if (timestamp >= lastStateChangeTimestamp) {
172             states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
173         } else {
174             ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
175                   (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
176             // The accumulated durations have become unreliable. For example, if the timestamp
177             // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
178             // we would get 4000, which is greater than (last - first). This could lead to
179             // counts exceeding 100%.
180             for (int i = 0; i < stateCount; i++) {
181                 states[i].timeInStateSinceUpdate = 0;
182             }
183         }
184     }
185     currentState = state;
186     lastStateChangeTimestamp = timestamp;
187 }
188 
189 template <class T>
setValue(state_t state,const T & value)190 void MultiStateCounter<T>::setValue(state_t state, const T& value) {
191     states[state].counter = value;
192 }
193 
194 template <class T>
updateValue(const T & value,time_t timestamp)195 const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
196     T* returnValue = &emptyValue;
197 
198     // If the counter is disabled, we ignore the update, except when the counter got disabled after
199     // the previous update, in which case we still need to pick up the residual delta.
200     if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
201         // If the update arrived out of order, just push back the timestamp to
202         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
203         if (timestamp < lastStateChangeTimestamp) {
204             timestamp = lastStateChangeTimestamp;
205         }
206 
207         // Confirm the current state for the side-effect of updating the time-in-state
208         // counter for the current state.
209         setState(currentState, timestamp);
210 
211         if (lastUpdateTimestamp >= 0) {
212             if (timestamp > lastUpdateTimestamp) {
213                 if (delta(lastValue, value, &deltaValue)) {
214                     returnValue = &deltaValue;
215                     time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
216                     for (int i = 0; i < stateCount; i++) {
217                         time_t timeInState = states[i].timeInStateSinceUpdate;
218                         if (timeInState) {
219                             add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
220                             states[i].timeInStateSinceUpdate = 0;
221                         }
222                     }
223                 } else {
224                     std::stringstream str;
225                     str << "updateValue is called with a value " << valueToString(value)
226                         << ", which is lower than the previous value " << valueToString(lastValue)
227                         << "\n";
228                     ALOGE("%s", str.str().c_str());
229 
230                     for (int i = 0; i < stateCount; i++) {
231                         states[i].timeInStateSinceUpdate = 0;
232                     }
233                 }
234             } else if (timestamp < lastUpdateTimestamp) {
235                 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
236                       (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
237 
238                 for (int i = 0; i < stateCount; i++) {
239                     states[i].timeInStateSinceUpdate = 0;
240                 }
241             }
242         }
243     }
244     lastValue = value;
245     lastUpdateTimestamp = timestamp;
246     return *returnValue;
247 }
248 
249 template <class T>
incrementValue(const T & increment,time_t timestamp)250 void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
251     T newValue = lastValue;
252     add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
253     updateValue(newValue, timestamp);
254 }
255 
256 template <class T>
addValue(const T & value)257 void MultiStateCounter<T>::addValue(const T& value) {
258     if (!isEnabled) {
259         return;
260     }
261     add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
262 }
263 
264 template <class T>
reset()265 void MultiStateCounter<T>::reset() {
266     lastStateChangeTimestamp = -1;
267     lastUpdateTimestamp = -1;
268     for (int i = 0; i < stateCount; i++) {
269         states[i].timeInStateSinceUpdate = 0;
270         states[i].counter = emptyValue;
271     }
272 }
273 
274 template <class T>
getStateCount()275 uint16_t MultiStateCounter<T>::getStateCount() {
276     return stateCount;
277 }
278 
279 template <class T>
getCount(state_t state)280 const T& MultiStateCounter<T>::getCount(state_t state) {
281     return states[state].counter;
282 }
283 
284 template <class T>
toString()285 std::string MultiStateCounter<T>::toString() {
286     std::stringstream str;
287     str << "[";
288     for (int i = 0; i < stateCount; i++) {
289         if (i != 0) {
290             str << ", ";
291         }
292         str << i << ": " << valueToString(states[i].counter);
293         if (states[i].timeInStateSinceUpdate > 0) {
294             str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
295         }
296     }
297     str << "]";
298     if (lastUpdateTimestamp >= 0) {
299         str << " updated: " << lastUpdateTimestamp;
300     }
301     if (lastStateChangeTimestamp >= 0) {
302         str << " currentState: " << currentState;
303         if (lastStateChangeTimestamp > lastUpdateTimestamp) {
304             str << " stateChanged: " << lastStateChangeTimestamp;
305         }
306     } else {
307         str << " currentState: none";
308     }
309     if (!isEnabled) {
310         str << " disabled";
311     }
312     return str.str();
313 }
314 
315 } // namespace battery
316 } // namespace android
317