One more feature in DurationMetric -- nesting on top of Or and Max
+ Added counters to count the starts.
Test: added some unit test in statsd_test.
TODO: ADD MORE UNIT TESTS
Change-Id: I3575e921a4abab27c4ea50fffde19cc1d4564030
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index aaf3ec2..e875acb 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -63,6 +63,7 @@
DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting,
const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension,
const uint64_t startTimeNs)
@@ -71,6 +72,7 @@
mStartIndex(startIndex),
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
+ mNested(nesting),
mInternalDimension(internalDimension) {
// TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
// them in the base class, because the proto generated CountMetric, and DurationMetric are
@@ -111,11 +113,11 @@
vector<DurationBucket>& bucket) {
switch (mMetric.type()) {
case DurationMetric_AggregationType_DURATION_SUM:
- return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex,
+ return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs,
bucket);
case DurationMetric_AggregationType_DURATION_MAX_SPARSE:
- return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex,
+ return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs,
bucket);
}
@@ -268,7 +270,7 @@
if (matcherIndex == mStartIndex) {
it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(atomKey, event.GetTimestampNs());
+ it->second->noteStop(atomKey, event.GetTimestampNs(), false);
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index eea00454..bb5d4d9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -39,7 +39,8 @@
public:
DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
const size_t startIndex, const size_t stopIndex,
- const size_t stopAllIndex, const sp<ConditionWizard>& wizard,
+ const size_t stopAllIndex, const bool nesting,
+ const sp<ConditionWizard>& wizard,
const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -80,6 +81,9 @@
// Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions.
const size_t mStopAllIndex;
+ // nest counting -- for the same key, stops must match the number of starts to make real stop
+ const bool mNested;
+
// The dimension from the atom predicate. e.g., uid, wakelock name.
const vector<KeyMatcher> mInternalDimension;
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 5c76d0e..18b3349 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -34,6 +34,10 @@
// Hold duration information for one atom level duration in current on-going bucket.
struct DurationInfo {
DurationState state;
+
+ // the number of starts seen.
+ int32_t startCount;
+
// most recent start time.
int64_t lastStartTime;
// existing duration in current bucket.
@@ -42,7 +46,7 @@
// cache the HashableDimensionKeys we need to query the condition for this duration event.
ConditionKey conditionKeys;
- DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){};
+ DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
};
struct DurationBucket {
@@ -53,18 +57,21 @@
class DurationTracker {
public:
- DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket)
+ DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ std::vector<DurationBucket>& bucket)
: mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
- mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucketSizeNs(bucketSizeNs),
+ mNested(nesting),
+ mCurrentBucketStartTimeNs(currentBucketStartNs),
mBucket(bucket),
mDuration(0){};
virtual ~DurationTracker(){};
virtual void noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
- virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0;
+ virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) = 0;
virtual void noteStopAll(const uint64_t eventTime) = 0;
virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
@@ -75,12 +82,14 @@
protected:
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
+ const int mConditionTrackerIndex;
+
+ const int64_t mBucketSizeNs;
+
+ const bool mNested;
uint64_t mCurrentBucketStartTimeNs;
- int64_t mBucketSizeNs;
-
std::vector<DurationBucket>& mBucket; // where to write output
int64_t mDuration; // current recorded duration result
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 43c21a8..8c7bfb6 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -23,10 +23,10 @@
namespace os {
namespace statsd {
-MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) {
+ : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket) {
}
void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
@@ -38,10 +38,10 @@
switch (duration.state) {
case kStarted:
- // The same event is already started. Because we are not counting nesting, so ignore.
+ duration.startCount++;
break;
case kPaused:
- // Safe to do nothing here. Paused means started but condition is false.
+ duration.startCount++;
break;
case kStopped:
if (!condition) {
@@ -51,11 +51,13 @@
duration.state = DurationState::kStarted;
duration.lastStartTime = eventTime;
}
+ duration.startCount = 1;
break;
}
}
-void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) {
+void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ bool forceStop) {
VLOG("MaxDuration: key %s stop", key.c_str());
if (mInfos.find(key) == mInfos.end()) {
// we didn't see a start event before. do nothing.
@@ -68,16 +70,23 @@
// already stopped, do nothing.
break;
case DurationState::kStarted: {
- duration.state = DurationState::kStopped;
- int64_t durationTime = eventTime - duration.lastStartTime;
- VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime,
- (long long)eventTime, (long long)durationTime);
- duration.lastDuration = duration.lastDuration + durationTime;
- VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ duration.state = DurationState::kStopped;
+ int64_t durationTime = eventTime - duration.lastStartTime;
+ VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(),
+ (long long)duration.lastStartTime, (long long)eventTime,
+ (long long)durationTime);
+ duration.lastDuration = duration.lastDuration + durationTime;
+ VLOG(" record duration: %lld ", (long long)duration.lastDuration);
+ }
break;
}
case DurationState::kPaused: {
- duration.state = DurationState::kStopped;
+ duration.startCount--;
+ if (forceStop || !mNested || duration.startCount <= 0) {
+ duration.state = DurationState::kStopped;
+ }
break;
}
}
@@ -88,11 +97,13 @@
}
// Once an atom duration ends, we erase it. Next time, if we see another atom event with the
// same name, they are still considered as different atom durations.
- mInfos.erase(key);
+ if (duration.state == DurationState::kStopped) {
+ mInfos.erase(key);
+ }
}
void MaxDurationTracker::noteStopAll(const uint64_t eventTime) {
for (auto& pair : mInfos) {
- noteStop(pair.first, eventTime);
+ noteStop(pair.first, eventTime, true);
}
}
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index b095884..167f81e 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,12 +28,13 @@
// they stop or bucket expires.
class MaxDurationTracker : public DurationTracker {
public:
- MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
std::vector<DurationBucket>& bucket);
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) override;
void noteStopAll(const uint64_t eventTime) override;
bool flushIfNeeded(uint64_t timestampNs) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index e4f1d21..faf5ce5 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -20,10 +20,14 @@
namespace android {
namespace os {
namespace statsd {
+
+using std::pair;
+
OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
- uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+ bool nesting, uint64_t currentBucketStartNs,
+ uint64_t bucketSizeNs,
std::vector<DurationBucket>& bucket)
- : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket),
+ : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket),
mStarted(),
mPaused() {
mLastStartTime = 0;
@@ -36,9 +40,9 @@
mLastStartTime = eventTime;
VLOG("record first start....");
}
- mStarted.insert(key);
+ mStarted[key]++;
} else {
- mPaused.insert(key);
+ mPaused[key]++;
}
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
@@ -48,11 +52,16 @@
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
-void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) {
+void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp,
+ const bool stopAll) {
VLOG("Oring: %s stop", key.c_str());
auto it = mStarted.find(key);
if (it != mStarted.end()) {
- mStarted.erase(it);
+ (it->second)--;
+ if (stopAll || !mNested || it->second <= 0) {
+ mStarted.erase(it);
+ mConditionKeyMap.erase(key);
+ }
if (mStarted.empty()) {
mDuration += (timestamp - mLastStartTime);
VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
@@ -60,8 +69,14 @@
}
}
- mPaused.erase(key);
- mConditionKeyMap.erase(key);
+ auto pausedIt = mPaused.find(key);
+ if (pausedIt != mPaused.end()) {
+ (pausedIt->second)--;
+ if (stopAll || !mNested || pausedIt->second <= 0) {
+ mPaused.erase(pausedIt);
+ mConditionKeyMap.erase(key);
+ }
+ }
}
void OringDurationTracker::noteStopAll(const uint64_t timestamp) {
if (!mStarted.empty()) {
@@ -118,11 +133,11 @@
}
void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
- vector<HashableDimensionKey> startedToPaused;
- vector<HashableDimensionKey> pausedToStarted;
+ vector<pair<HashableDimensionKey, int>> startedToPaused;
+ vector<pair<HashableDimensionKey, int>> pausedToStarted;
if (!mStarted.empty()) {
for (auto it = mStarted.begin(); it != mStarted.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -130,8 +145,8 @@
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
ConditionState::kTrue) {
+ startedToPaused.push_back(*it);
it = mStarted.erase(it);
- startedToPaused.push_back(key);
VLOG("Key %s started -> paused", key.c_str());
} else {
++it;
@@ -147,7 +162,7 @@
if (!mPaused.empty()) {
for (auto it = mPaused.begin(); it != mPaused.end();) {
- auto key = *it;
+ const auto& key = it->first;
if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
VLOG("Key %s dont have condition key", key.c_str());
++it;
@@ -155,8 +170,8 @@
}
if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
ConditionState::kTrue) {
+ pausedToStarted.push_back(*it);
it = mPaused.erase(it);
- pausedToStarted.push_back(key);
VLOG("Key %s paused -> started", key.c_str());
} else {
++it;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index b54dafa..78760ba 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,12 +27,13 @@
// Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
class OringDurationTracker : public DurationTracker {
public:
- OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex,
+ OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
std::vector<DurationBucket>& bucket);
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
- void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override;
+ void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
+ const bool stopAll) override;
void noteStopAll(const uint64_t eventTime) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
@@ -44,8 +45,8 @@
// 2) which keys are paused (started but condition was false)
// 3) whenever a key stops, we remove it from the started set. And if the set becomes empty,
// it means everything has stopped, we then record the end time.
- std::set<HashableDimensionKey> mStarted;
- std::set<HashableDimensionKey> mPaused;
+ std::map<HashableDimensionKey, int> mStarted;
+ std::map<HashableDimensionKey, int> mPaused;
int64_t mLastStartTime;
std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
};
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index c2044d8..ba9ea67 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -247,6 +247,8 @@
const auto& simpleCondition = durationWhat.simple_condition();
+ bool nesting = simpleCondition.count_nesting();
+
int trackerIndices[3] = {-1, -1, -1};
if (!simpleCondition.has_start() ||
!handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
@@ -284,7 +286,7 @@
sp<MetricProducer> durationMetric = new DurationMetricProducer(
metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
- wizard, internalDimension, startTimeNs);
+ nesting, wizard, internalDimension, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 58bf1b3..9cc184a 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -45,13 +45,13 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
tracker.noteStart("", true, bucketStartTimeNs, key1);
- tracker.noteStop("", bucketStartTimeNs + 10);
+ tracker.noteStop("", bucketStartTimeNs + 10, false);
tracker.noteStart("", true, bucketStartTimeNs + 20, key1);
- tracker.noteStop("", bucketStartTimeNs + 40);
+ tracker.noteStop("", bucketStartTimeNs + 40, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
@@ -67,7 +67,7 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets);
tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
@@ -77,6 +77,37 @@
EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
}
+TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ vector<DurationBucket> buckets;
+ ConditionKey key1;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ MaxDurationTracker tracker(wizard, -1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ // 2 starts
+ tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+ tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+ // one stop
+ tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(2u, buckets.size());
+ EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration);
+ EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration);
+
+ // real stop now.
+ tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+
+ EXPECT_EQ(3u, buckets.size());
+ EXPECT_EQ(5, buckets[2].mDuration);
+}
+
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -93,13 +124,13 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t durationTimeNs = 2 * 1000;
- MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ MaxDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 74a6f11..f495d6b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -47,18 +47,43 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
}
+TEST(OringDurationTrackerTest, TestDurationNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(2003, buckets[0].mDuration);
+}
+
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -75,18 +100,50 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets);
+ OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets);
tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
- tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs);
+ tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1u, buckets.size());
EXPECT_EQ(5, buckets[0].mDuration);
}
+
+TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ ConditionKey key1;
+ key1["APP_BACKGROUND"] = "1:maps|";
+
+ EXPECT_CALL(*wizard, query(_, key1)) // #4
+ .WillOnce(Return(ConditionState::kFalse));
+
+ vector<DurationBucket> buckets;
+
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
+ uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+ OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets);
+
+ tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+ tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
+
+ tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+
+ tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+
+ tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
+ EXPECT_EQ(1u, buckets.size());
+ EXPECT_EQ(15, buckets[0].mDuration);
+}
+
} // namespace statsd
} // namespace os
} // namespace android