blob: 04b718698ec9742166a274c67219a9ab842eff32 [file] [log] [blame]
Dmitri Plotnikov5effd852021-08-11 14:55:58 -07001/*
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 */
31namespace android {
32namespace battery {
33
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -080034#define REPORTED_INVALID_TIMESTAMP_DELTA_MS 60000
35
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070036typedef uint16_t state_t;
37
38template <class T>
39class 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;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070047 bool isEnabled;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070048
49 struct State {
50 time_t timeInStateSinceUpdate;
51 T counter;
52 };
53
54 State* states;
55
56public:
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070057 MultiStateCounter(uint16_t stateCount, const T& emptyValue);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070058
59 virtual ~MultiStateCounter();
60
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070061 void setEnabled(bool enabled, time_t timestamp);
62
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070063 void setState(state_t state, time_t timestamp);
64
Dmitri Plotnikov756365b2024-03-25 17:39:59 -070065 /**
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
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070071 void setValue(state_t state, const T& value);
72
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070073 /**
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080074 * 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.
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070077 */
78 const T& updateValue(const T& value, time_t timestamp);
79
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080080 /**
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);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070092
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070093 void reset();
94
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070095 uint16_t getStateCount();
96
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070097 const T& getCount(state_t state);
98
99 std::string toString();
100
101private:
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
122template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700123MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700124 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700125 currentState(0),
126 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700127 emptyValue(emptyValue),
128 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700129 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700130 deltaValue(emptyValue),
131 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700132 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
139template <class T>
140MultiStateCounter<T>::~MultiStateCounter() {
141 delete[] states;
142};
143
144template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700145void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
146 if (enabled == isEnabled) {
147 return;
148 }
149
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700150 if (isEnabled) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700151 // Confirm the current state for the side-effect of updating the time-in-state
152 // counter for the current state.
153 setState(currentState, timestamp);
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700154 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 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700162
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700163 if (lastStateChangeTimestamp >= 0) {
164 lastStateChangeTimestamp = timestamp;
165 }
166 isEnabled = true;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700167 }
168}
169
170template <class T>
171void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700172 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
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700179 if (timestamp >= lastStateChangeTimestamp) {
180 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
181 } else {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800182 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
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700188 // 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 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700195 }
196 }
197 currentState = state;
198 lastStateChangeTimestamp = timestamp;
199}
200
201template <class T>
Dmitri Plotnikov756365b2024-03-25 17:39:59 -0700202void 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
217template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700218void MultiStateCounter<T>::setValue(state_t state, const T& value) {
219 states[state].counter = value;
220}
221
222template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700223const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700224 T* returnValue = &emptyValue;
225
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700226 // 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) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700229 // 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
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700235 // Confirm the current state for the side-effect of updating the time-in-state
236 // counter for the current state.
237 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700238
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700239 if (lastUpdateTimestamp >= 0) {
240 if (timestamp > lastUpdateTimestamp) {
241 if (delta(lastValue, value, &deltaValue)) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700242 returnValue = &deltaValue;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700243 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 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700250 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700251 } 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());
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800257
258 for (int i = 0; i < stateCount; i++) {
259 states[i].timeInStateSinceUpdate = 0;
260 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700261 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700262 } else if (timestamp < lastUpdateTimestamp) {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800263 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 }
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800267
268 for (int i = 0; i < stateCount; i++) {
269 states[i].timeInStateSinceUpdate = 0;
270 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700271 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700272 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700273 }
274 lastValue = value;
275 lastUpdateTimestamp = timestamp;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700276 return *returnValue;
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700277}
278
279template <class T>
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -0800280void 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
286template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700287void MultiStateCounter<T>::addValue(const T& value) {
288 if (!isEnabled) {
289 return;
290 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700291 add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700292}
293
294template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700295void 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
304template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700305uint16_t MultiStateCounter<T>::getStateCount() {
306 return stateCount;
307}
308
309template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700310const T& MultiStateCounter<T>::getCount(state_t state) {
311 return states[state].counter;
312}
313
314template <class T>
315std::string MultiStateCounter<T>::toString() {
316 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700317 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700318 for (int i = 0; i < stateCount; i++) {
319 if (i != 0) {
320 str << ", ";
321 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700322 str << i << ": " << valueToString(states[i].counter);
323 if (states[i].timeInStateSinceUpdate > 0) {
324 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
325 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700326 }
327 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700328 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 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700339 if (!isEnabled) {
340 str << " disabled";
341 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700342 return str.str();
343}
344
345} // namespace battery
346} // namespace android