blob: a2b59a877f519680a6519744662dcb2128184e68 [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 /**
66 * Updates the value for the current state and returns the delta from the previously
67 * set value.
68 */
69 const T& updateValue(const T& value, time_t timestamp);
70
71 void addValue(const T& value);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070072
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070073 void reset();
74
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070075 uint16_t getStateCount();
76
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070077 const T& getCount(state_t state);
78
79 std::string toString();
80
81private:
82 /**
83 * Subtracts previousValue from newValue and returns the result in outValue.
84 * Returns true iff the combination of previousValue and newValue is valid
85 * (newValue >= prevValue)
86 */
87 bool delta(const T& previousValue, const T& newValue, T* outValue) const;
88
89 /**
90 * Adds value2 to value1 and stores the result in value1. Denominator is
91 * guaranteed to be non-zero.
92 */
93 void add(T* value1, const T& value2, const uint64_t numerator,
94 const uint64_t denominator) const;
95
96 std::string valueToString(const T& value) const;
97};
98
99// ---------------------- MultiStateCounter Implementation -------------------------
100// Since MultiStateCounter is a template, the implementation must be inlined.
101
102template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700103MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700104 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700105 currentState(0),
106 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700107 emptyValue(emptyValue),
108 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700109 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700110 deltaValue(emptyValue),
111 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700112 states = new State[stateCount];
113 for (int i = 0; i < stateCount; i++) {
114 states[i].timeInStateSinceUpdate = 0;
115 states[i].counter = emptyValue;
116 }
117}
118
119template <class T>
120MultiStateCounter<T>::~MultiStateCounter() {
121 delete[] states;
122};
123
124template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700125void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
126 if (enabled == isEnabled) {
127 return;
128 }
129
130 if (!enabled) {
131 // Confirm the current state for the side-effect of updating the time-in-state
132 // counter for the current state.
133 setState(currentState, timestamp);
134 }
135
136 isEnabled = enabled;
137
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700138 if (lastStateChangeTimestamp >= 0) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700139 lastStateChangeTimestamp = timestamp;
140 }
141}
142
143template <class T>
144void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
145 if (isEnabled && lastStateChangeTimestamp >= 0) {
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700146 if (timestamp >= lastStateChangeTimestamp) {
147 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
148 } else {
149 ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
150 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
151 // The accumulated durations have become unreliable. For example, if the timestamp
152 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
153 // we would get 4000, which is greater than (last - first). This could lead to
154 // counts exceeding 100%.
155 for (int i = 0; i < stateCount; i++) {
156 states[i].timeInStateSinceUpdate = 0;
157 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700158 }
159 }
160 currentState = state;
161 lastStateChangeTimestamp = timestamp;
162}
163
164template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700165void MultiStateCounter<T>::setValue(state_t state, const T& value) {
166 states[state].counter = value;
167}
168
169template <class T>
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700170const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700171 T* returnValue = &emptyValue;
172
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700173 // If the counter is disabled, we ignore the update, except when the counter got disabled after
174 // the previous update, in which case we still need to pick up the residual delta.
175 if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
176 // Confirm the current state for the side-effect of updating the time-in-state
177 // counter for the current state.
178 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700179
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700180 if (lastUpdateTimestamp >= 0) {
181 if (timestamp > lastUpdateTimestamp) {
182 if (delta(lastValue, value, &deltaValue)) {
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700183 returnValue = &deltaValue;
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700184 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
185 for (int i = 0; i < stateCount; i++) {
186 time_t timeInState = states[i].timeInStateSinceUpdate;
187 if (timeInState) {
188 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
189 states[i].timeInStateSinceUpdate = 0;
190 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700191 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700192 } else {
193 std::stringstream str;
194 str << "updateValue is called with a value " << valueToString(value)
195 << ", which is lower than the previous value " << valueToString(lastValue)
196 << "\n";
197 ALOGE("%s", str.str().c_str());
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800198
199 for (int i = 0; i < stateCount; i++) {
200 states[i].timeInStateSinceUpdate = 0;
201 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700202 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700203 } else if (timestamp < lastUpdateTimestamp) {
204 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
205 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
Dmitri Plotnikov4b238fb2021-11-09 11:52:08 -0800206
207 for (int i = 0; i < stateCount; i++) {
208 states[i].timeInStateSinceUpdate = 0;
209 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700210 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700211 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700212 }
213 lastValue = value;
214 lastUpdateTimestamp = timestamp;
Dmitri Plotnikov41f56632021-09-28 17:21:54 -0700215 return *returnValue;
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700216}
217
218template <class T>
219void MultiStateCounter<T>::addValue(const T& value) {
220 if (!isEnabled) {
221 return;
222 }
223
224 add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700225}
226
227template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700228void MultiStateCounter<T>::reset() {
229 lastStateChangeTimestamp = -1;
230 lastUpdateTimestamp = -1;
231 for (int i = 0; i < stateCount; i++) {
232 states[i].timeInStateSinceUpdate = 0;
233 states[i].counter = emptyValue;
234 }
235}
236
237template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700238uint16_t MultiStateCounter<T>::getStateCount() {
239 return stateCount;
240}
241
242template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700243const T& MultiStateCounter<T>::getCount(state_t state) {
244 return states[state].counter;
245}
246
247template <class T>
248std::string MultiStateCounter<T>::toString() {
249 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700250 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700251 for (int i = 0; i < stateCount; i++) {
252 if (i != 0) {
253 str << ", ";
254 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700255 str << i << ": " << valueToString(states[i].counter);
256 if (states[i].timeInStateSinceUpdate > 0) {
257 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
258 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700259 }
260 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700261 if (lastUpdateTimestamp >= 0) {
262 str << " updated: " << lastUpdateTimestamp;
263 }
264 if (lastStateChangeTimestamp >= 0) {
265 str << " currentState: " << currentState;
266 if (lastStateChangeTimestamp > lastUpdateTimestamp) {
267 str << " stateChanged: " << lastStateChangeTimestamp;
268 }
269 } else {
270 str << " currentState: none";
271 }
Dmitri Plotnikov8940e5d2021-09-23 14:56:30 -0700272 if (!isEnabled) {
273 str << " disabled";
274 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700275 return str.str();
276}
277
278} // namespace battery
279} // namespace android