blob: ce9cd1cebe1cf4794a3675ef9c8c8bb8a9e5803b [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
34typedef uint16_t state_t;
35
36template <class T>
37class 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;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070045 bool isEnabled;
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070046
47 struct State {
48 time_t timeInStateSinceUpdate;
49 T counter;
50 };
51
52 State* states;
53
54public:
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070055 MultiStateCounter(uint16_t stateCount, const T& emptyValue);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070056
57 virtual ~MultiStateCounter();
58
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070059 void setEnabled(bool enabled, time_t timestamp);
60
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070061 void setState(state_t state, time_t timestamp);
62
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070063 void setValue(state_t state, const T& value);
64
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070065 /**
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080066 * 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.
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -070069 */
70 const T& updateValue(const T& value, time_t timestamp);
71
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -080072 /**
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);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070084
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070085 void reset();
86
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070087 uint16_t getStateCount();
88
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070089 const T& getCount(state_t state);
90
91 std::string toString();
92
93private:
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
114template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700115MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700116 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700117 currentState(0),
118 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700119 emptyValue(emptyValue),
120 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700121 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700122 deltaValue(emptyValue),
123 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700124 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
131template <class T>
132MultiStateCounter<T>::~MultiStateCounter() {
133 delete[] states;
134};
135
136template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700137void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
138 if (enabled == isEnabled) {
139 return;
140 }
141
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700142 if (isEnabled) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700143 // Confirm the current state for the side-effect of updating the time-in-state
144 // counter for the current state.
145 setState(currentState, timestamp);
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700146 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 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700154
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700155 if (lastStateChangeTimestamp >= 0) {
156 lastStateChangeTimestamp = timestamp;
157 }
158 isEnabled = true;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700159 }
160}
161
162template <class T>
163void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700164 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
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700171 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 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700183 }
184 }
185 currentState = state;
186 lastStateChangeTimestamp = timestamp;
187}
188
189template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700190void MultiStateCounter<T>::setValue(state_t state, const T& value) {
191 states[state].counter = value;
192}
193
194template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700195const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700196 T* returnValue = &emptyValue;
197
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700198 // 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) {
Dmitri Plotnikov8ca08ab2022-04-26 21:10:34 -0700201 // 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
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700207 // Confirm the current state for the side-effect of updating the time-in-state
208 // counter for the current state.
209 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700210
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700211 if (lastUpdateTimestamp >= 0) {
212 if (timestamp > lastUpdateTimestamp) {
213 if (delta(lastValue, value, &deltaValue)) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700214 returnValue = &deltaValue;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700215 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 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700222 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700223 } 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());
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800229
230 for (int i = 0; i < stateCount; i++) {
231 states[i].timeInStateSinceUpdate = 0;
232 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700233 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700234 } else if (timestamp < lastUpdateTimestamp) {
235 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
236 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800237
238 for (int i = 0; i < stateCount; i++) {
239 states[i].timeInStateSinceUpdate = 0;
240 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700241 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700242 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700243 }
244 lastValue = value;
245 lastUpdateTimestamp = timestamp;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700246 return *returnValue;
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700247}
248
249template <class T>
Dmitri Plotnikov40dcce22021-12-10 15:38:17 -0800250void 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
256template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700257void MultiStateCounter<T>::addValue(const T& value) {
258 if (!isEnabled) {
259 return;
260 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700261 add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700262}
263
264template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700265void 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
274template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700275uint16_t MultiStateCounter<T>::getStateCount() {
276 return stateCount;
277}
278
279template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700280const T& MultiStateCounter<T>::getCount(state_t state) {
281 return states[state].counter;
282}
283
284template <class T>
285std::string MultiStateCounter<T>::toString() {
286 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700287 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700288 for (int i = 0; i < stateCount; i++) {
289 if (i != 0) {
290 str << ", ";
291 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700292 str << i << ": " << valueToString(states[i].counter);
293 if (states[i].timeInStateSinceUpdate > 0) {
294 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
295 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700296 }
297 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700298 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 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700309 if (!isEnabled) {
310 str << " disabled";
311 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700312 return str.str();
313}
314
315} // namespace battery
316} // namespace android