1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include <gtest/gtest_prod.h> 19 #include <stdint.h> 20 #include <algorithm> 21 22 namespace android { 23 namespace os { 24 namespace statsd { 25 26 /** 27 * A simple stopwatch to time the duration of condition being true. 28 * 29 * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition 30 * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps 31 * should be elapsedRealTime in nano seconds. 32 * 33 * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is 34 * responsible for thread safety. 35 */ 36 class ConditionTimer { 37 public: ConditionTimer(bool initCondition,int64_t bucketStartNs)38 explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) { 39 if (initCondition) { 40 mLastConditionChangeTimestampNs = bucketStartNs; 41 } 42 }; 43 44 // Tracks how long the condition has been stayed true in the *current* bucket. 45 // When a new bucket is created, this value will be reset to 0. 46 int64_t mTimerNs = 0; 47 48 /** Tracks the delay prior current bucket start due to delayed bucket close. */ 49 int64_t mCurrentBucketStartDelayNs = 0; 50 51 // Last elapsed real timestamp when condition changed. 52 int64_t mLastConditionChangeTimestampNs = 0; 53 54 bool mCondition = false; 55 56 struct ConditionDurationInfo { 57 int64_t mDurationNs; 58 int64_t mCorrectionNs; 59 60 inline bool operator==(const ConditionDurationInfo& that) const { 61 return mDurationNs == that.mDurationNs && mCorrectionNs == that.mCorrectionNs; 62 } 63 }; 64 65 /** 66 * Handles new bucket event processing and performs condition duration calculation 67 * In case if next bucket start timestamp differs from event timestamp, the 68 * correction calculation will be performed, due to delayed bucket close 69 * \param eventTimeNs current timestamp 70 * \param nextBucketStartNs next bucket start expected timestamp 71 * \return The condition duration and correction in nanoseconds for the previous bucket 72 */ newBucketStart(int64_t eventTimeNs,int64_t nextBucketStartNs)73 ConditionDurationInfo newBucketStart(int64_t eventTimeNs, int64_t nextBucketStartNs) { 74 // we would like to apply correction only in case 75 // - when condition was true before new bucket start (pull event often the case) 76 // - and remains true after the edge 77 // here the mCondition represents current condition, which could be updated 78 // based on onConditionChange() event 79 80 int64_t conditionCorrectionNs = -mCurrentBucketStartDelayNs; 81 mCurrentBucketStartDelayNs = 0; 82 83 const int64_t currentBucketEndDelayNs = 84 std::max(eventTimeNs - nextBucketStartNs, (int64_t)0); 85 86 if (mCondition) { 87 // Normally, the next bucket happens after the last condition 88 // change. In this case, add the time between the condition becoming 89 // true to the next bucket start time. 90 // Otherwise, the next bucket start time is before the last 91 // condition change time, this means that the condition was false at 92 // the bucket boundary before the condition became true, so the 93 // timer should not get updated and the last condition change time 94 // remains as is. 95 if (nextBucketStartNs >= mLastConditionChangeTimestampNs) { 96 mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs); 97 mLastConditionChangeTimestampNs = nextBucketStartNs; 98 conditionCorrectionNs += currentBucketEndDelayNs; 99 100 // keep start delay correction for the next bucket - condition was true 101 // before the edge and remains true after the edge 102 mCurrentBucketStartDelayNs = currentBucketEndDelayNs; 103 } 104 } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) { 105 // The next bucket start time is before the last condition change 106 // time, this means that the condition was true at the bucket 107 // boundary before the condition became false, so adjust the timer 108 // to match how long the condition was true to the bucket boundary. 109 // This means remove the amount the condition stayed true in the 110 // next bucket from the current bucket. 111 mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs); 112 conditionCorrectionNs += currentBucketEndDelayNs; 113 114 // keep start delay correction for the next bucket - condition was true 115 // before the edge and remains true after the edge up to delay 116 mCurrentBucketStartDelayNs = currentBucketEndDelayNs; 117 } 118 119 const int64_t conditionDurationNs = mTimerNs; 120 mTimerNs = 0; 121 122 if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) { 123 // The next bucket start time is before the last condition change 124 // time, this means that the condition was true at the bucket 125 // boundary and remained true in the next bucket up to the condition 126 // change to false, so adjust the timer to match how long the 127 // condition stayed true in the next bucket (now the current bucket). 128 mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs; 129 } 130 return {conditionDurationNs, conditionCorrectionNs}; 131 } 132 onConditionChanged(bool newCondition,int64_t timestampNs)133 void onConditionChanged(bool newCondition, int64_t timestampNs) { 134 if (newCondition == mCondition) { 135 return; 136 } 137 mCondition = newCondition; 138 if (newCondition == false) { 139 mTimerNs += (timestampNs - mLastConditionChangeTimestampNs); 140 } 141 mLastConditionChangeTimestampNs = timestampNs; 142 } 143 144 FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False); 145 FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True); 146 FRIEND_TEST(ConditionTimerTest, TestTimer_Correction_DelayedChangeToFalse); 147 FRIEND_TEST(ConditionTimerTest, TestTimer_Correction_DelayedChangeToTrue); 148 FRIEND_TEST(ConditionTimerTest, TestTimer_Correction_DelayedWithInitialFalse); 149 FRIEND_TEST(ConditionTimerTest, TestTimer_Correction_DelayedWithInitialTrue); 150 }; 151 152 } // namespace statsd 153 } // namespace os 154 } // namespace android 155