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