blob: fadc4ffd4100995edbf6cd33ef6d328fbd51ae3d [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
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080038template <class T, class V>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070039class MultiStateCounter {
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080040 const uint16_t stateCount;
41 const V emptyValue;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070042 state_t currentState;
43 time_t lastStateChangeTimestamp;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070044 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 Plotnikov0a6dc402024-01-19 14:33:20 -080057 MultiStateCounter(uint16_t stateCount, const V& 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 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080069 void copyStatesFrom(const MultiStateCounter<T, V> &source);
Dmitri Plotnikov756365b2024-03-25 17:39:59 -070070
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080071 void setValue(state_t state, const V& value);
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070072
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 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080078 const V& updateValue(const V& value, time_t timestamp);
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070079
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 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080084 void incrementValue(const V& increment, time_t timestamp);
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080085
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 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -080091 void addValue(const V& 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 Plotnikov0a6dc402024-01-19 14:33:20 -080097 const V& getCount(state_t state);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070098
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 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800107 bool delta(const T& previousValue, const V& newValue, T* outValue) const;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700108
109 /**
110 * Adds value2 to value1 and stores the result in value1. Denominator is
111 * guaranteed to be non-zero.
112 */
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800113 void add(T* value1, const V& value2, const uint64_t numerator,
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700114 const uint64_t denominator) const;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700115};
116
117// ---------------------- MultiStateCounter Implementation -------------------------
118// Since MultiStateCounter is a template, the implementation must be inlined.
119
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800120template <class T, class V>
121MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700122 : stateCount(stateCount),
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800123 emptyValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700124 currentState(0),
125 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700126 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700127 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700128 deltaValue(emptyValue),
129 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700130 states = new State[stateCount];
131 for (int i = 0; i < stateCount; i++) {
132 states[i].timeInStateSinceUpdate = 0;
133 states[i].counter = emptyValue;
134 }
135}
136
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800137template <class T, class V>
138MultiStateCounter<T, V>::~MultiStateCounter() {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700139 delete[] states;
140};
141
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800142template <class T, class V>
143void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700144 if (enabled == isEnabled) {
145 return;
146 }
147
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700148 if (isEnabled) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700149 // Confirm the current state for the side-effect of updating the time-in-state
150 // counter for the current state.
151 setState(currentState, timestamp);
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700152 isEnabled = false;
153 } else {
154 // If the counter is being enabled with an out-of-order timestamp, just push back
155 // the timestamp to avoid having the situation where
156 // timeInStateSinceUpdate > timeSinceUpdate
157 if (timestamp < lastUpdateTimestamp) {
158 timestamp = lastUpdateTimestamp;
159 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700160
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700161 if (lastStateChangeTimestamp >= 0) {
162 lastStateChangeTimestamp = timestamp;
163 }
164 isEnabled = true;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700165 }
166}
167
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800168template <class T, class V>
169void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700170 if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
171 // If the update arrived out-of-order, just push back the timestamp to
172 // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
173 if (timestamp < lastUpdateTimestamp) {
174 timestamp = lastUpdateTimestamp;
175 }
176
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700177 if (timestamp >= lastStateChangeTimestamp) {
178 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
179 } else {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800180 if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
181 ALOGE("setState is called with an earlier timestamp: %lu, "
182 "previous timestamp: %lu\n",
183 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
184 }
185
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700186 // The accumulated durations have become unreliable. For example, if the timestamp
187 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
188 // we would get 4000, which is greater than (last - first). This could lead to
189 // counts exceeding 100%.
190 for (int i = 0; i < stateCount; i++) {
191 states[i].timeInStateSinceUpdate = 0;
192 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700193 }
194 }
195 currentState = state;
196 lastStateChangeTimestamp = timestamp;
197}
198
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800199template <class T, class V>
200void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
Dmitri Plotnikov756365b2024-03-25 17:39:59 -0700201 if (stateCount != source.stateCount) {
202 ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
203 return;
204 }
205
206 currentState = source.currentState;
207 for (int i = 0; i < stateCount; i++) {
208 states[i].timeInStateSinceUpdate = source.states[i].timeInStateSinceUpdate;
209 states[i].counter = emptyValue;
210 }
211 lastStateChangeTimestamp = source.lastStateChangeTimestamp;
212 lastUpdateTimestamp = source.lastUpdateTimestamp;
213}
214
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800215template <class T, class V>
216void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700217 states[state].counter = value;
218}
219
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800220template <class T, class V>
221const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
222 const V* returnValue = &emptyValue;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700223
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700224 // If the counter is disabled, we ignore the update, except when the counter got disabled after
225 // the previous update, in which case we still need to pick up the residual delta.
226 if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700227 // If the update arrived out of order, just push back the timestamp to
228 // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
229 if (timestamp < lastStateChangeTimestamp) {
230 timestamp = lastStateChangeTimestamp;
231 }
232
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700233 // Confirm the current state for the side-effect of updating the time-in-state
234 // counter for the current state.
235 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700236
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700237 if (lastUpdateTimestamp >= 0) {
238 if (timestamp > lastUpdateTimestamp) {
239 if (delta(lastValue, value, &deltaValue)) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700240 returnValue = &deltaValue;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700241 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
242 for (int i = 0; i < stateCount; i++) {
243 time_t timeInState = states[i].timeInStateSinceUpdate;
244 if (timeInState) {
245 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
246 states[i].timeInStateSinceUpdate = 0;
247 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700248 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700249 } else {
250 std::stringstream str;
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800251 str << "updateValue is called with a value " << value
252 << ", which is lower than the previous value " << lastValue
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700253 << "\n";
254 ALOGE("%s", str.str().c_str());
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800255
256 for (int i = 0; i < stateCount; i++) {
257 states[i].timeInStateSinceUpdate = 0;
258 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700259 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700260 } else if (timestamp < lastUpdateTimestamp) {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800261 if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
262 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
263 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
264 }
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800265
266 for (int i = 0; i < stateCount; i++) {
267 states[i].timeInStateSinceUpdate = 0;
268 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700269 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700270 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700271 }
272 lastValue = value;
273 lastUpdateTimestamp = timestamp;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700274 return *returnValue;
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700275}
276
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800277template <class T, class V>
278void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
279// T newValue;
280// newValue = lastValue; // Copy assignment, not initialization.
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -0800281 T newValue = lastValue;
282 add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
283 updateValue(newValue, timestamp);
284}
285
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800286template <class T, class V>
287void MultiStateCounter<T, V>::addValue(const V& value) {
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700288 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
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800294template <class T, class V>
295void MultiStateCounter<T, V>::reset() {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700296 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
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800304template <class T, class V>
305uint16_t MultiStateCounter<T, V>::getStateCount() {
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700306 return stateCount;
307}
308
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800309template <class T, class V>
310const V& MultiStateCounter<T, V>::getCount(state_t state) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700311 return states[state].counter;
312}
313
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800314template <class T, class V>
315std::string MultiStateCounter<T, V>::toString() {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700316 std::stringstream str;
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800317// str << "LAST VALUE: " << valueToString(lastValue);
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700318 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700319 for (int i = 0; i < stateCount; i++) {
320 if (i != 0) {
321 str << ", ";
322 }
Dmitri Plotnikov0a6dc402024-01-19 14:33:20 -0800323 str << i << ": " << states[i].counter;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700324 if (states[i].timeInStateSinceUpdate > 0) {
325 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
326 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700327 }
328 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700329 if (lastUpdateTimestamp >= 0) {
330 str << " updated: " << lastUpdateTimestamp;
331 }
332 if (lastStateChangeTimestamp >= 0) {
333 str << " currentState: " << currentState;
334 if (lastStateChangeTimestamp > lastUpdateTimestamp) {
335 str << " stateChanged: " << lastStateChangeTimestamp;
336 }
337 } else {
338 str << " currentState: none";
339 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700340 if (!isEnabled) {
341 str << " disabled";
342 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700343 return str.str();
344}
345
346} // namespace battery
347} // namespace android