blob: e1ee07ccf12dac6b580c30b1500c64bcb913e46d [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 Plotnikov5effd852021-08-11 14:55:58 -070065 void updateValue(const T& value, time_t timestamp);
66
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -070067 void reset();
68
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070069 uint16_t getStateCount();
70
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070071 const T& getCount(state_t state);
72
73 std::string toString();
74
75private:
76 /**
77 * Subtracts previousValue from newValue and returns the result in outValue.
78 * Returns true iff the combination of previousValue and newValue is valid
79 * (newValue >= prevValue)
80 */
81 bool delta(const T& previousValue, const T& newValue, T* outValue) const;
82
83 /**
84 * Adds value2 to value1 and stores the result in value1. Denominator is
85 * guaranteed to be non-zero.
86 */
87 void add(T* value1, const T& value2, const uint64_t numerator,
88 const uint64_t denominator) const;
89
90 std::string valueToString(const T& value) const;
91};
92
93// ---------------------- MultiStateCounter Implementation -------------------------
94// Since MultiStateCounter is a template, the implementation must be inlined.
95
96template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070097MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070098 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070099 currentState(0),
100 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700101 emptyValue(emptyValue),
102 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700103 lastUpdateTimestamp(-1),
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700104 deltaValue(emptyValue),
105 isEnabled(true) {
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700106 states = new State[stateCount];
107 for (int i = 0; i < stateCount; i++) {
108 states[i].timeInStateSinceUpdate = 0;
109 states[i].counter = emptyValue;
110 }
111}
112
113template <class T>
114MultiStateCounter<T>::~MultiStateCounter() {
115 delete[] states;
116};
117
118template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700119void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
120 if (enabled == isEnabled) {
121 return;
122 }
123
124 if (!enabled) {
125 // Confirm the current state for the side-effect of updating the time-in-state
126 // counter for the current state.
127 setState(currentState, timestamp);
128 }
129
130 isEnabled = enabled;
131
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700132 if (lastStateChangeTimestamp >= 0) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700133 lastStateChangeTimestamp = timestamp;
134 }
135}
136
137template <class T>
138void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
139 if (isEnabled && lastStateChangeTimestamp >= 0) {
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700140 if (timestamp >= lastStateChangeTimestamp) {
141 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
142 } else {
143 ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
144 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
145 // The accumulated durations have become unreliable. For example, if the timestamp
146 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
147 // we would get 4000, which is greater than (last - first). This could lead to
148 // counts exceeding 100%.
149 for (int i = 0; i < stateCount; i++) {
150 states[i].timeInStateSinceUpdate = 0;
151 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700152 }
153 }
154 currentState = state;
155 lastStateChangeTimestamp = timestamp;
156}
157
158template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700159void MultiStateCounter<T>::setValue(state_t state, const T& value) {
160 states[state].counter = value;
161}
162
163template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700164void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700165 // If the counter is disabled, we ignore the update, except when the counter got disabled after
166 // the previous update, in which case we still need to pick up the residual delta.
167 if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
168 // Confirm the current state for the side-effect of updating the time-in-state
169 // counter for the current state.
170 setState(currentState, timestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700171
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700172 if (lastUpdateTimestamp >= 0) {
173 if (timestamp > lastUpdateTimestamp) {
174 if (delta(lastValue, value, &deltaValue)) {
175 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
176 for (int i = 0; i < stateCount; i++) {
177 time_t timeInState = states[i].timeInStateSinceUpdate;
178 if (timeInState) {
179 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
180 states[i].timeInStateSinceUpdate = 0;
181 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700182 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700183 } else {
184 std::stringstream str;
185 str << "updateValue is called with a value " << valueToString(value)
186 << ", which is lower than the previous value " << valueToString(lastValue)
187 << "\n";
188 ALOGE("%s", str.str().c_str());
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700189 }
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700190 } else if (timestamp < lastUpdateTimestamp) {
191 ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
192 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700193 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700194 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700195 }
196 lastValue = value;
197 lastUpdateTimestamp = timestamp;
198}
199
200template <class T>
Dmitri Plotnikov99d7c712021-09-03 19:07:23 -0700201void MultiStateCounter<T>::reset() {
202 lastStateChangeTimestamp = -1;
203 lastUpdateTimestamp = -1;
204 for (int i = 0; i < stateCount; i++) {
205 states[i].timeInStateSinceUpdate = 0;
206 states[i].counter = emptyValue;
207 }
208}
209
210template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700211uint16_t MultiStateCounter<T>::getStateCount() {
212 return stateCount;
213}
214
215template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700216const T& MultiStateCounter<T>::getCount(state_t state) {
217 return states[state].counter;
218}
219
220template <class T>
221std::string MultiStateCounter<T>::toString() {
222 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700223 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700224 for (int i = 0; i < stateCount; i++) {
225 if (i != 0) {
226 str << ", ";
227 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700228 str << i << ": " << valueToString(states[i].counter);
229 if (states[i].timeInStateSinceUpdate > 0) {
230 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
231 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700232 }
233 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700234 if (lastUpdateTimestamp >= 0) {
235 str << " updated: " << lastUpdateTimestamp;
236 }
237 if (lastStateChangeTimestamp >= 0) {
238 str << " currentState: " << currentState;
239 if (lastStateChangeTimestamp > lastUpdateTimestamp) {
240 str << " stateChanged: " << lastStateChangeTimestamp;
241 }
242 } else {
243 str << " currentState: none";
244 }
245
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700246 return str.str();
247}
248
249} // namespace battery
250} // namespace android