blob: 7da8d51ccdb7039cbabc11751126771c6163cc88 [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 Plotnikov12aaf8e2021-09-03 19:07:23 -070065 void setValue(state_t state, const T& value);
66
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070067 /**
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080068 * Updates the value by distributing the delta from the previously set value
69 * among states according to their respective time-in-state.
70 * Returns the delta from the previously set value.
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070071 */
72 const T& updateValue(const T& value, time_t timestamp);
73
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080074 /**
75 * Updates the value by distributing the specified increment among states according
76 * to their respective time-in-state.
77 */
78 void incrementValue(const T& increment, time_t timestamp);
79
80 /**
81 * Adds the specified increment to the value for the current state, without affecting
82 * the last updated value or timestamp. Ignores partial time-in-state: the entirety of
83 * the increment is given to the current state.
84 */
85 void addValue(const T& increment);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070086
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070087 void reset();
88
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070089 uint16_t getStateCount();
90
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070091 const T& getCount(state_t state);
92
93 std::string toString();
94
95private:
96 /**
97 * Subtracts previousValue from newValue and returns the result in outValue.
98 * Returns true iff the combination of previousValue and newValue is valid
99 * (newValue >= prevValue)
100 */
101 bool delta(const T& previousValue, const T& newValue, T* outValue) const;
102
103 /**
104 * Adds value2 to value1 and stores the result in value1. Denominator is
105 * guaranteed to be non-zero.
106 */
107 void add(T* value1, const T& value2, const uint64_t numerator,
108 const uint64_t denominator) const;
109
110 std::string valueToString(const T& value) const;
111};
112
113// ---------------------- MultiStateCounter Implementation -------------------------
114// Since MultiStateCounter is a template, the implementation must be inlined.
115
116template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700117MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700118 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700119 currentState(0),
120 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700121 emptyValue(emptyValue),
122 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700123 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700124 deltaValue(emptyValue),
125 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700126 states = new State[stateCount];
127 for (int i = 0; i < stateCount; i++) {
128 states[i].timeInStateSinceUpdate = 0;
129 states[i].counter = emptyValue;
130 }
131}
132
133template <class T>
134MultiStateCounter<T>::~MultiStateCounter() {
135 delete[] states;
136};
137
138template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700139void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
140 if (enabled == isEnabled) {
141 return;
142 }
143
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700144 if (isEnabled) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700145 // Confirm the current state for the side-effect of updating the time-in-state
146 // counter for the current state.
147 setState(currentState, timestamp);
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700148 isEnabled = false;
149 } else {
150 // If the counter is being enabled with an out-of-order timestamp, just push back
151 // the timestamp to avoid having the situation where
152 // timeInStateSinceUpdate > timeSinceUpdate
153 if (timestamp < lastUpdateTimestamp) {
154 timestamp = lastUpdateTimestamp;
155 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700156
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700157 if (lastStateChangeTimestamp >= 0) {
158 lastStateChangeTimestamp = timestamp;
159 }
160 isEnabled = true;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700161 }
162}
163
164template <class T>
165void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700166 if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
167 // If the update arrived out-of-order, just push back the timestamp to
168 // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
169 if (timestamp < lastUpdateTimestamp) {
170 timestamp = lastUpdateTimestamp;
171 }
172
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700173 if (timestamp >= lastStateChangeTimestamp) {
174 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
175 } else {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800176 if (timestamp < lastStateChangeTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
177 ALOGE("setState is called with an earlier timestamp: %lu, "
178 "previous timestamp: %lu\n",
179 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
180 }
181
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700182 // The accumulated durations have become unreliable. For example, if the timestamp
183 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
184 // we would get 4000, which is greater than (last - first). This could lead to
185 // counts exceeding 100%.
186 for (int i = 0; i < stateCount; i++) {
187 states[i].timeInStateSinceUpdate = 0;
188 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700189 }
190 }
191 currentState = state;
192 lastStateChangeTimestamp = timestamp;
193}
194
195template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700196void MultiStateCounter<T>::setValue(state_t state, const T& value) {
197 states[state].counter = value;
198}
199
200template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700201const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700202 T* returnValue = &emptyValue;
203
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700204 // If the counter is disabled, we ignore the update, except when the counter got disabled after
205 // the previous update, in which case we still need to pick up the residual delta.
206 if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700207 // If the update arrived out of order, just push back the timestamp to
208 // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
209 if (timestamp < lastStateChangeTimestamp) {
210 timestamp = lastStateChangeTimestamp;
211 }
212
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700213 // Confirm the current state for the side-effect of updating the time-in-state
214 // counter for the current state.
215 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700216
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700217 if (lastUpdateTimestamp >= 0) {
218 if (timestamp > lastUpdateTimestamp) {
219 if (delta(lastValue, value, &deltaValue)) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700220 returnValue = &deltaValue;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700221 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
222 for (int i = 0; i < stateCount; i++) {
223 time_t timeInState = states[i].timeInStateSinceUpdate;
224 if (timeInState) {
225 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
226 states[i].timeInStateSinceUpdate = 0;
227 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700228 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700229 } else {
230 std::stringstream str;
231 str << "updateValue is called with a value " << valueToString(value)
232 << ", which is lower than the previous value " << valueToString(lastValue)
233 << "\n";
234 ALOGE("%s", str.str().c_str());
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800235
236 for (int i = 0; i < stateCount; i++) {
237 states[i].timeInStateSinceUpdate = 0;
238 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700239 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700240 } else if (timestamp < lastUpdateTimestamp) {
Dmitri Plotnikov1d728ef2024-01-19 14:33:20 -0800241 if (timestamp < lastUpdateTimestamp - REPORTED_INVALID_TIMESTAMP_DELTA_MS) {
242 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
243 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
244 }
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800245
246 for (int i = 0; i < stateCount; i++) {
247 states[i].timeInStateSinceUpdate = 0;
248 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700249 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700250 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700251 }
252 lastValue = value;
253 lastUpdateTimestamp = timestamp;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700254 return *returnValue;
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700255}
256
257template <class T>
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -0800258void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
259 T newValue = lastValue;
260 add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
261 updateValue(newValue, timestamp);
262}
263
264template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700265void MultiStateCounter<T>::addValue(const T& value) {
266 if (!isEnabled) {
267 return;
268 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700269 add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700270}
271
272template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700273void MultiStateCounter<T>::reset() {
274 lastStateChangeTimestamp = -1;
275 lastUpdateTimestamp = -1;
276 for (int i = 0; i < stateCount; i++) {
277 states[i].timeInStateSinceUpdate = 0;
278 states[i].counter = emptyValue;
279 }
280}
281
282template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700283uint16_t MultiStateCounter<T>::getStateCount() {
284 return stateCount;
285}
286
287template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700288const T& MultiStateCounter<T>::getCount(state_t state) {
289 return states[state].counter;
290}
291
292template <class T>
293std::string MultiStateCounter<T>::toString() {
294 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700295 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700296 for (int i = 0; i < stateCount; i++) {
297 if (i != 0) {
298 str << ", ";
299 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700300 str << i << ": " << valueToString(states[i].counter);
301 if (states[i].timeInStateSinceUpdate > 0) {
302 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
303 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700304 }
305 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700306 if (lastUpdateTimestamp >= 0) {
307 str << " updated: " << lastUpdateTimestamp;
308 }
309 if (lastStateChangeTimestamp >= 0) {
310 str << " currentState: " << currentState;
311 if (lastStateChangeTimestamp > lastUpdateTimestamp) {
312 str << " stateChanged: " << lastStateChangeTimestamp;
313 }
314 } else {
315 str << " currentState: none";
316 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700317 if (!isEnabled) {
318 str << " disabled";
319 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700320 return str.str();
321}
322
323} // namespace battery
324} // namespace android