blob: 40de068a95fe46dc4fb9f7cfbc7311d1c28302bc [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;
45
46 struct State {
47 time_t timeInStateSinceUpdate;
48 T counter;
49 };
50
51 State* states;
52
53public:
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070054 MultiStateCounter(uint16_t stateCount, const T& emptyValue);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070055
56 virtual ~MultiStateCounter();
57
58 void setState(state_t state, time_t timestamp);
59
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070060 void setValue(state_t state, const T& value);
61
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070062 void updateValue(const T& value, time_t timestamp);
63
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070064 uint16_t getStateCount();
65
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070066 const T& getCount(state_t state);
67
68 std::string toString();
69
70private:
71 /**
72 * Subtracts previousValue from newValue and returns the result in outValue.
73 * Returns true iff the combination of previousValue and newValue is valid
74 * (newValue >= prevValue)
75 */
76 bool delta(const T& previousValue, const T& newValue, T* outValue) const;
77
78 /**
79 * Adds value2 to value1 and stores the result in value1. Denominator is
80 * guaranteed to be non-zero.
81 */
82 void add(T* value1, const T& value2, const uint64_t numerator,
83 const uint64_t denominator) const;
84
85 std::string valueToString(const T& value) const;
86};
87
88// ---------------------- MultiStateCounter Implementation -------------------------
89// Since MultiStateCounter is a template, the implementation must be inlined.
90
91template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070092MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070093 : stateCount(stateCount),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070094 currentState(0),
95 lastStateChangeTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070096 emptyValue(emptyValue),
97 lastValue(emptyValue),
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -070098 lastUpdateTimestamp(-1),
Dmitri Plotnikov5effd852021-08-11 14:55:58 -070099 deltaValue(emptyValue) {
100 states = new State[stateCount];
101 for (int i = 0; i < stateCount; i++) {
102 states[i].timeInStateSinceUpdate = 0;
103 states[i].counter = emptyValue;
104 }
105}
106
107template <class T>
108MultiStateCounter<T>::~MultiStateCounter() {
109 delete[] states;
110};
111
112template <class T>
113void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700114 if (lastStateChangeTimestamp >= 0) {
115 if (timestamp >= lastStateChangeTimestamp) {
116 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
117 } else {
118 ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
119 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
120 // The accumulated durations have become unreliable. For example, if the timestamp
121 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
122 // we would get 4000, which is greater than (last - first). This could lead to
123 // counts exceeding 100%.
124 for (int i = 0; i < stateCount; i++) {
125 states[i].timeInStateSinceUpdate = 0;
126 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700127 }
128 }
129 currentState = state;
130 lastStateChangeTimestamp = timestamp;
131}
132
133template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700134void MultiStateCounter<T>::setValue(state_t state, const T& value) {
135 states[state].counter = value;
136}
137
138template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700139void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
140 // Confirm the current state for the side-effect of updating the time-in-state
141 // counter for the current state.
142 setState(currentState, timestamp);
143
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700144 if (lastUpdateTimestamp >= 0) {
145 if (timestamp > lastUpdateTimestamp) {
146 if (delta(lastValue, value, &deltaValue)) {
147 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
148 for (int i = 0; i < stateCount; i++) {
149 time_t timeInState = states[i].timeInStateSinceUpdate;
150 if (timeInState) {
151 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
152 states[i].timeInStateSinceUpdate = 0;
153 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700154 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700155 } else {
156 std::stringstream str;
157 str << "updateValue is called with a value " << valueToString(value)
158 << ", which is lower than the previous value " << valueToString(lastValue)
159 << "\n";
160 ALOGE("%s", str.str().c_str());
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700161 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700162 } else if (timestamp < lastUpdateTimestamp) {
163 ALOGE("updateValue is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
164 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700165 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700166 }
167 lastValue = value;
168 lastUpdateTimestamp = timestamp;
169}
170
171template <class T>
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700172uint16_t MultiStateCounter<T>::getStateCount() {
173 return stateCount;
174}
175
176template <class T>
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700177const T& MultiStateCounter<T>::getCount(state_t state) {
178 return states[state].counter;
179}
180
181template <class T>
182std::string MultiStateCounter<T>::toString() {
183 std::stringstream str;
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700184 str << "[";
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700185 for (int i = 0; i < stateCount; i++) {
186 if (i != 0) {
187 str << ", ";
188 }
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700189 str << i << ": " << valueToString(states[i].counter);
190 if (states[i].timeInStateSinceUpdate > 0) {
191 str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
192 }
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700193 }
194 str << "]";
Dmitri Plotnikov12aaf8e2021-09-03 19:07:23 -0700195 if (lastUpdateTimestamp >= 0) {
196 str << " updated: " << lastUpdateTimestamp;
197 }
198 if (lastStateChangeTimestamp >= 0) {
199 str << " currentState: " << currentState;
200 if (lastStateChangeTimestamp > lastUpdateTimestamp) {
201 str << " stateChanged: " << lastStateChangeTimestamp;
202 }
203 } else {
204 str << " currentState: none";
205 }
206
Dmitri Plotnikov5effd852021-08-11 14:55:58 -0700207 return str.str();
208}
209
210} // namespace battery
211} // namespace android