blob: 9f56b29dfbed79b2929c60f6eba9be856878e693 [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:
54 MultiStateCounter(uint16_t stateCount, state_t initialState, const T& emptyValue,
55 time_t timestamp);
56
57 virtual ~MultiStateCounter();
58
59 void setState(state_t state, time_t timestamp);
60
61 void updateValue(const T& value, time_t timestamp);
62
63 const T& getCount(state_t state);
64
65 std::string toString();
66
67private:
68 /**
69 * Subtracts previousValue from newValue and returns the result in outValue.
70 * Returns true iff the combination of previousValue and newValue is valid
71 * (newValue >= prevValue)
72 */
73 bool delta(const T& previousValue, const T& newValue, T* outValue) const;
74
75 /**
76 * Adds value2 to value1 and stores the result in value1. Denominator is
77 * guaranteed to be non-zero.
78 */
79 void add(T* value1, const T& value2, const uint64_t numerator,
80 const uint64_t denominator) const;
81
82 std::string valueToString(const T& value) const;
83};
84
85// ---------------------- MultiStateCounter Implementation -------------------------
86// Since MultiStateCounter is a template, the implementation must be inlined.
87
88template <class T>
89MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, state_t initialState,
90 const T& emptyValue, time_t timestamp)
91 : stateCount(stateCount),
92 currentState(initialState),
93 lastStateChangeTimestamp(timestamp),
94 emptyValue(emptyValue),
95 lastValue(emptyValue),
96 lastUpdateTimestamp(timestamp),
97 deltaValue(emptyValue) {
98 states = new State[stateCount];
99 for (int i = 0; i < stateCount; i++) {
100 states[i].timeInStateSinceUpdate = 0;
101 states[i].counter = emptyValue;
102 }
103}
104
105template <class T>
106MultiStateCounter<T>::~MultiStateCounter() {
107 delete[] states;
108};
109
110template <class T>
111void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
112 if (timestamp >= lastStateChangeTimestamp) {
113 states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
114 } else {
115 ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
116 (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
117 // The accumulated durations have become unreliable. For example, if the timestamp
118 // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
119 // we would get 4000, which is greater than (last - first). This could lead to
120 // counts exceeding 100%.
121 for (int i = 0; i < stateCount; i++) {
122 states[i].timeInStateSinceUpdate = 0;
123 }
124 }
125 currentState = state;
126 lastStateChangeTimestamp = timestamp;
127}
128
129template <class T>
130void MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
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 if (timestamp > lastUpdateTimestamp) {
136 if (delta(lastValue, value, &deltaValue)) {
137 time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
138 for (int i = 0; i < stateCount; i++) {
139 time_t timeInState = states[i].timeInStateSinceUpdate;
140 if (timeInState) {
141 add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
142 states[i].timeInStateSinceUpdate = 0;
143 }
144 }
145 } else {
146 std::stringstream str;
147 str << "updateValue is called with a value " << valueToString(value)
148 << ", which is lower than the previous value " << valueToString(lastValue) << "\n";
149 ALOGE("%s", str.str().c_str());
150 }
151 } else if (timestamp < lastUpdateTimestamp) {
152 ALOGE("updateValue is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
153 (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
154 }
155 lastValue = value;
156 lastUpdateTimestamp = timestamp;
157}
158
159template <class T>
160const T& MultiStateCounter<T>::getCount(state_t state) {
161 return states[state].counter;
162}
163
164template <class T>
165std::string MultiStateCounter<T>::toString() {
166 std::stringstream str;
167 str << "currentState: " << currentState
168 << " lastStateChangeTimestamp: " << lastStateChangeTimestamp
169 << " lastUpdateTimestamp: " << lastUpdateTimestamp << " states: [";
170 for (int i = 0; i < stateCount; i++) {
171 if (i != 0) {
172 str << ", ";
173 }
174 str << i << ": time: " << states[i].timeInStateSinceUpdate
175 << " counter: " << valueToString(states[i].counter);
176 }
177 str << "]";
178 return str.str();
179}
180
181} // namespace battery
182} // namespace android