1/ Support nested message and repeated fields in statsd.
2/ Filter gauge fields by FieldMatcher.
3/ Wire up wakelock attribution chain.
4/ e2e test: wakelock duration metric with aggregated predicate dimensions.
5/ e2e test: count metric with multiple metric condition links for 2 predicates and 1 non-sliced predicate.
Test: statsd unit test passed.
Change-Id: I89db31cb068184a54e0a892fad710966d3127bc9
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index c392540..a365f54 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -20,6 +20,9 @@
src/stats_log.proto \
src/statsd_config.proto \
src/atoms.proto \
+ src/field_util.cpp \
+ src/stats_log_util.cpp \
+ src/dimension.cpp \
src/anomaly/AnomalyMonitor.cpp \
src/anomaly/AnomalyTracker.cpp \
src/anomaly/DurationAnomalyTracker.cpp \
@@ -163,6 +166,7 @@
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
+ tests/LogEvent_test.cpp \
tests/MetricsManager_test.cpp \
tests/StatsLogProcessor_test.cpp \
tests/UidMap_test.cpp \
@@ -176,7 +180,10 @@
tests/metrics/ValueMetricProducer_test.cpp \
tests/metrics/GaugeMetricProducer_test.cpp \
tests/guardrail/StatsdStats_test.cpp \
- tests/metrics/metrics_test_helper.cpp
+ tests/metrics/metrics_test_helper.cpp \
+ tests/statsd_test_util.cpp \
+ tests/e2e/WakelockDuration_e2e_test.cpp \
+ tests/e2e/MetricConditionLink_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
@@ -198,4 +205,4 @@
##############################
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 0b6f8f2..6da1243 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -13,92 +13,104 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
#include "HashableDimensionKey.h"
+#include "dimension.h"
namespace android {
namespace os {
namespace statsd {
+android::hash_t hashDimensionsValue(const DimensionsValue& value) {
+ android::hash_t hash = 0;
+ hash = android::JenkinsHashMix(hash, android::hash_type(value.field()));
+
+ hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case()));
+ switch (value.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ hash = android::JenkinsHashMix(
+ hash,
+ static_cast<uint32_t>(std::hash<std::string>()(value.value_str())));
+ break;
+ case DimensionsValue::ValueCase::kValueInt:
+ hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int()));
+ break;
+ case DimensionsValue::ValueCase::kValueLong:
+ hash = android::JenkinsHashMix(
+ hash, android::hash_type(static_cast<int64_t>(value.value_long())));
+ break;
+ case DimensionsValue::ValueCase::kValueBool:
+ hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool()));
+ break;
+ case DimensionsValue::ValueCase::kValueFloat: {
+ float floatVal = value.value_float();
+ hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+ break;
+ }
+ case DimensionsValue::ValueCase::kValueTuple: {
+ hash = android::JenkinsHashMix(hash, android::hash_type(
+ value.value_tuple().dimensions_value_size()));
+ for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+ hash = android::JenkinsHashMix(
+ hash,
+ hashDimensionsValue(value.value_tuple().dimensions_value(i)));
+ }
+ break;
+ }
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ break;
+ }
+ return JenkinsHashWhiten(hash);
+}
+
using std::string;
+
string HashableDimensionKey::toString() const {
string flattened;
- for (const auto& pair : mKeyValuePairs) {
- flattened += std::to_string(pair.key());
- flattened += ":";
- switch (pair.value_case()) {
- case KeyValuePair::ValueCase::kValueStr:
- flattened += pair.value_str();
- break;
- case KeyValuePair::ValueCase::kValueInt:
- flattened += std::to_string(pair.value_int());
- break;
- case KeyValuePair::ValueCase::kValueLong:
- flattened += std::to_string(pair.value_long());
- break;
- case KeyValuePair::ValueCase::kValueBool:
- flattened += std::to_string(pair.value_bool());
- break;
- case KeyValuePair::ValueCase::kValueFloat:
- flattened += std::to_string(pair.value_float());
- break;
- default:
- break;
- }
- flattened += "|";
- }
+ DimensionsValueToString(getDimensionsValue(), &flattened);
return flattened;
}
-bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
- const auto& keyValue2 = that.getKeyValuePairs();
- if (mKeyValuePairs.size() != keyValue2.size()) {
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+ if (s1.field() != s2.field()) {
return false;
}
-
- for (size_t i = 0; i < keyValue2.size(); i++) {
- const auto& kv1 = mKeyValuePairs[i];
- const auto& kv2 = keyValue2[i];
- if (kv1.key() != kv2.key()) {
- return false;
- }
-
- if (kv1.value_case() != kv2.value_case()) {
- return false;
- }
-
- switch (kv1.value_case()) {
- case KeyValuePair::ValueCase::kValueStr:
- if (kv1.value_str() != kv2.value_str()) {
- return false;
- }
- break;
- case KeyValuePair::ValueCase::kValueInt:
- if (kv1.value_int() != kv2.value_int()) {
- return false;
- }
- break;
- case KeyValuePair::ValueCase::kValueLong:
- if (kv1.value_long() != kv2.value_long()) {
- return false;
- }
- break;
- case KeyValuePair::ValueCase::kValueBool:
- if (kv1.value_bool() != kv2.value_bool()) {
- return false;
- }
- break;
- case KeyValuePair::ValueCase::kValueFloat: {
- if (kv1.value_float() != kv2.value_float()) {
- return false;
- }
- break;
- }
- case KeyValuePair::ValueCase::VALUE_NOT_SET:
- break;
- }
+ if (s1.value_case() != s1.value_case()) {
+ return false;
}
- return true;
+ switch (s1.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ return (s1.value_str() == s2.value_str());
+ case DimensionsValue::ValueCase::kValueInt:
+ return s1.value_int() == s2.value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ return s1.value_long() == s2.value_long();
+ case DimensionsValue::ValueCase::kValueBool:
+ return s1.value_bool() == s2.value_bool();
+ case DimensionsValue::ValueCase::kValueFloat:
+ return s1.value_float() == s2.value_float();
+ case DimensionsValue::ValueCase::kValueTuple:
+ {
+ if (s1.value_tuple().dimensions_value_size() !=
+ s2.value_tuple().dimensions_value_size()) {
+ return false;
+ }
+ bool allMatched = true;
+ for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
+ allMatched &= compareDimensionsValue(s1.value_tuple().dimensions_value(i),
+ s2.value_tuple().dimensions_value(i));
+ }
+ return allMatched;
+ }
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ default:
+ return true;
+ }
+}
+
+bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
+ return compareDimensionsValue(getDimensionsValue(), that.getDimensionsValue());
};
bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85215552..3a4ffce 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -25,20 +25,20 @@
class HashableDimensionKey {
public:
- explicit HashableDimensionKey(const std::vector<KeyValuePair>& keyValuePairs)
- : mKeyValuePairs(keyValuePairs){};
+ explicit HashableDimensionKey(const DimensionsValue& dimensionsValue)
+ : mDimensionsValue(dimensionsValue){};
HashableDimensionKey(){};
HashableDimensionKey(const HashableDimensionKey& that)
- : mKeyValuePairs(that.getKeyValuePairs()){};
+ : mDimensionsValue(that.getDimensionsValue()){};
HashableDimensionKey& operator=(const HashableDimensionKey& from) = default;
std::string toString() const;
- inline const std::vector<KeyValuePair>& getKeyValuePairs() const {
- return mKeyValuePairs;
+ inline const DimensionsValue& getDimensionsValue() const {
+ return mDimensionsValue;
}
bool operator==(const HashableDimensionKey& that) const;
@@ -50,9 +50,11 @@
}
private:
- std::vector<KeyValuePair> mKeyValuePairs;
+ DimensionsValue mDimensionsValue;
};
+android::hash_t hashDimensionsValue(const DimensionsValue& value);
+
} // namespace statsd
} // namespace os
} // namespace android
@@ -60,42 +62,11 @@
namespace std {
using android::os::statsd::HashableDimensionKey;
-using android::os::statsd::KeyValuePair;
template <>
struct hash<HashableDimensionKey> {
std::size_t operator()(const HashableDimensionKey& key) const {
- android::hash_t hash = 0;
- for (const auto& pair : key.getKeyValuePairs()) {
- hash = android::JenkinsHashMix(hash, android::hash_type(pair.key()));
- hash = android::JenkinsHashMix(
- hash, android::hash_type(static_cast<int32_t>(pair.value_case())));
- switch (pair.value_case()) {
- case KeyValuePair::ValueCase::kValueStr:
- hash = android::JenkinsHashMix(
- hash,
- static_cast<uint32_t>(std::hash<std::string>()(pair.value_str())));
- break;
- case KeyValuePair::ValueCase::kValueInt:
- hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_int()));
- break;
- case KeyValuePair::ValueCase::kValueLong:
- hash = android::JenkinsHashMix(
- hash, android::hash_type(static_cast<int64_t>(pair.value_long())));
- break;
- case KeyValuePair::ValueCase::kValueBool:
- hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_bool()));
- break;
- case KeyValuePair::ValueCase::kValueFloat: {
- float floatVal = pair.value_float();
- hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
- break;
- }
- case KeyValuePair::ValueCase::VALUE_NOT_SET:
- break;
- }
- }
- return hash;
+ return hashDimensionsValue(key.getDimensionsValue());
}
};
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 5f9b53a..13f332e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -63,11 +63,12 @@
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const sp<AnomalyMonitor>& anomalyMonitor,
+ const long timeBaseSec,
const std::function<void(const ConfigKey&)>& sendBroadcast)
: mUidMap(uidMap),
mAnomalyMonitor(anomalyMonitor),
mSendBroadcast(sendBroadcast),
- mTimeBaseSec(time(nullptr)) {
+ mTimeBaseSec(timeBaseSec) {
// On each initialization of StatsLogProcessor, check stats-data directory to see if there is
// any left over data to be read.
StorageManager::sendBroadcast(STATS_DATA_DIR, mSendBroadcast);
@@ -97,7 +98,6 @@
pair.second->onLogEvent(msg);
flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second));
}
-
// Hard-coded logic to update the isolated uid's in the uid-map.
// The field numbers need to be currently updated by hand with atoms.proto
if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
@@ -117,9 +117,7 @@
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
ALOGD("Updated configuration for key %s", key.ToString().c_str());
-
sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
-
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
ALOGE("Can't accept more configs!");
@@ -152,6 +150,19 @@
return it->second->byteSize();
}
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) {
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGW("Config source %s does not exist", key.ToString().c_str());
+ return;
+ }
+ report->mutable_config_key()->set_uid(key.GetUid());
+ report->mutable_config_key()->set_name(key.GetName());
+ ConfigMetricsReport* configMetricsReport = report->add_reports();
+ it->second->onDumpReport(dumpTimeStampNs, configMetricsReport);
+ // TODO: dump uid mapping.
+}
+
void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1e5c426..f62fc4e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef STATS_LOG_PROCESSOR_H
-#define STATS_LOG_PROCESSOR_H
+#pragma once
+
+#include <gtest/gtest_prod.h>
#include "config/ConfigListener.h"
#include "logd/LogReader.h"
#include "metrics/MetricsManager.h"
@@ -33,6 +34,7 @@
class StatsLogProcessor : public ConfigListener {
public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+ const long timeBaseSec,
const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
@@ -44,6 +46,7 @@
size_t GetMetricsSize(const ConfigKey& key) const;
void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
+ void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report);
/* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
void onAnomalyAlarmFired(
@@ -81,10 +84,12 @@
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+ FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
+
};
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATS_LOG_PROCESSOR_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 76e2e48..e8b0bd2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -75,7 +75,7 @@
{
mUidMap = new UidMap();
mConfigManager = new ConfigManager();
- mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) {
+ mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1c6d9b0..1ee86f0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -104,24 +104,18 @@
}
/**
- * An attribution represents an application or module that is part of process where a particular bit
- * of work is done.
+ * This proto represents a node of an attribution chain.
+ * Note: All attribution chains are represented as a repeated field of type
+ * AttributionNode. It is understood that in such arrays, the order is that
+ * of calls, that is [A, B, C] if A calls B that calls C.
*/
-message Attribution {
- // The uid for an application or module.
+message AttributionNode {
+ // The uid for a given element in the attribution chain.
optional int32 uid = 1;
- // The string tag for the attribution node.
- optional string tag = 2;
-}
-/**
- * An attribution chain represents the chained attributions of applications or modules that
- * resulted in a particular bit of work being done.
- * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that
- * calls C.
- */
-message AttributionChain {
- repeated Attribution attribution = 1;
+ // The (optional) string tag for an element in the attribution chain. If the
+ // element has no tag, it is encoded as an empty string.
+ optional string tag = 2;
}
/*
@@ -151,7 +145,7 @@
*/
message AttributionChainDummyAtom {
- optional AttributionChain attribution_chain = 1;
+ repeated AttributionNode attribution_node = 1;
optional int32 value = 2;
}
@@ -421,8 +415,7 @@
* TODO
*/
message WakelockStateChanged {
- // TODO: Add attribution instead of uid.
- optional int32 uid = 1;
+ repeated AttributionNode attribution_node = 1;
// Type of wakelock.
enum Type {
@@ -780,13 +773,14 @@
* frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
*/
message ActivityForegroundStateChanged {
+ optional int32 uid = 1;
+ optional string pkg_name = 2;
+ optional string class_name = 3;
+
enum Activity {
MOVE_TO_BACKGROUND = 0;
MOVE_TO_FOREGROUND = 1;
}
- optional int32 uid = 1;
- optional string pkg_name = 2;
- optional string class_name = 3;
optional Activity activity = 4;
}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index bb4b817..52b83d8 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -103,7 +103,7 @@
}
void CombinationConditionTracker::isConditionMet(
- const map<string, HashableDimensionKey>& conditionParameters,
+ const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
vector<ConditionState>& conditionCache) const {
for (const int childIndex : mChildren) {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 9336914..8942833 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,7 +41,7 @@
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
- void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
+ void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) const override;
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 6f66ad6..1154b6f 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -42,6 +42,8 @@
virtual ~ConditionTracker(){};
+ inline const string& getName() { return mName; }
+
// Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
// be done in the constructor, but we do it separately because (1) easy to return a bool to
// indicate whether the initialization is successful. (2) makes unit test easier.
@@ -83,7 +85,7 @@
// done recursively
// [conditionCache]: the cache holding the condition evaluation values.
virtual void isConditionMet(
- const std::map<std::string, HashableDimensionKey>& conditionParameters,
+ const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) const = 0;
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index 411f7e5..d99c2cc 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -24,7 +24,7 @@
using std::vector;
ConditionState ConditionWizard::query(const int index,
- const map<string, HashableDimensionKey>& parameters) {
+ const ConditionKey& parameters) {
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 30a3684..4ff5c07 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -19,6 +19,7 @@
#include "ConditionTracker.h"
#include "condition_util.h"
+#include "stats_util.h"
namespace android {
namespace os {
@@ -40,7 +41,7 @@
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(
const int conditionIndex,
- const std::map<std::string, HashableDimensionKey>& conditionParameters);
+ const ConditionKey& conditionParameters);
private:
std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index a63bc04..1803cbb 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -76,10 +76,9 @@
mStopAllLogMatcherIndex = -1;
}
- mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(),
- simplePredicate.dimension().end());
+ mOutputDimensions = simplePredicate.dimensions();
- if (mOutputDimension.size() > 0) {
+ if (mOutputDimensions.child_size() > 0) {
mSliced = true;
}
@@ -150,6 +149,14 @@
bool matchStart,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& conditionChangedCache) {
+ if ((int)conditionChangedCache.size() <= mIndex) {
+ ALOGE("handleConditionEvent: param conditionChangedCache not initialized.");
+ return;
+ }
+ if ((int)conditionCache.size() <= mIndex) {
+ ALOGE("handleConditionEvent: param conditionCache not initialized.");
+ return;
+ }
bool changed = false;
auto outputIt = mSlicedConditionState.find(outputKey);
ConditionState newCondition;
@@ -278,36 +285,64 @@
return;
}
- // outputKey is the output key values. e.g, uid:1234
- const HashableDimensionKey outputKey(getDimensionKey(event, mOutputDimension));
- handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
+ // outputKey is the output values. e.g, uid:1234
+ const std::vector<DimensionsValue> outputValues = getDimensionKeys(event, mOutputDimensions);
+ if (outputValues.size() == 0) {
+ // The original implementation would generate an empty string dimension hash when condition
+ // is not sliced.
+ handleConditionEvent(
+ DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache);
+ } else if (outputValues.size() == 1) {
+ handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1,
+ conditionCache, conditionChangedCache);
+ } else {
+ // If this event has multiple nodes in the attribution chain, this log event probably will
+ // generate multiple dimensions. If so, we will find if the condition changes for any
+ // dimension and ask the corresponding metric producer to verify whether the actual sliced
+ // condition has changed or not.
+ // A high level assumption is that a predicate is either sliced or unsliced. We will never
+ // have both sliced and unsliced version of a predicate.
+ for (const DimensionsValue& outputValue : outputValues) {
+ vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
+ ConditionState::kNotEvaluated);
+ vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
+
+ handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
+ dimensionalConditionCache, dimensionalConditionChangedCache);
+
+ OrConditionState(dimensionalConditionCache, &conditionCache);
+ OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
+ }
+ }
}
void SimpleConditionTracker::isConditionMet(
- const map<string, HashableDimensionKey>& conditionParameters,
+ const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
vector<ConditionState>& conditionCache) const {
const auto pair = conditionParameters.find(mName);
- HashableDimensionKey key =
- (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
- if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+ if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
mName.c_str());
conditionCache[mIndex] = mInitialValue;
return;
}
+ std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+ const std::vector<HashableDimensionKey> &keys =
+ (pair == conditionParameters.end()) ? defaultKeys : pair->second;
- VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str());
-
- auto startedCountIt = mSlicedConditionState.find(key);
- if (startedCountIt == mSlicedConditionState.end()) {
- conditionCache[mIndex] = mInitialValue;
- } else {
- conditionCache[mIndex] =
- startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ ConditionState conditionState = ConditionState::kNotEvaluated;
+ for (const auto& key : keys) {
+ auto startedCountIt = mSlicedConditionState.find(key);
+ if (startedCountIt != mSlicedConditionState.end()) {
+ conditionState = conditionState |
+ (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ } else {
+ conditionState = conditionState | mInitialValue;
+ }
}
-
+ conditionCache[mIndex] = conditionState;
VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
}
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 644d84c..5048635 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -46,7 +46,7 @@
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
- void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
+ void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
std::vector<ConditionState>& conditionCache) const override;
@@ -66,7 +66,7 @@
ConditionState mInitialValue;
- std::vector<KeyMatcher> mOutputDimension;
+ FieldMatcher mOutputDimensions;
std::map<HashableDimensionKey, int> mSlicedConditionState;
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 53ef9d5..a5aee73 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -27,6 +27,7 @@
#include "ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
+#include "dimension.h"
namespace android {
namespace os {
@@ -93,23 +94,106 @@
return newCondition;
}
-HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
- const MetricConditionLink& link) {
- vector<KeyMatcher> eventKey;
- eventKey.reserve(link.key_in_what().size());
+ConditionState operator|(ConditionState l, ConditionState r) {
+ return l >= r ? l : r;
+}
- for (const auto& key : link.key_in_what()) {
- eventKey.push_back(key);
+void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored) {
+ if (ref.size() != ored->size()) {
+ return;
+ }
+ for (size_t i = 0; i < ored->size(); ++i) {
+ ored->at(i) = ored->at(i) | ref.at(i);
+ }
+}
+
+void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) {
+ if (ref.size() != ored->size()) {
+ return;
+ }
+ for (size_t i = 0; i < ored->size(); ++i) {
+ ored->at(i) = ored->at(i) | ref.at(i);
+ }
+}
+
+void getFieldsFromFieldMatcher(const FieldMatcher& matcher, const Field& parentField,
+ std::vector<Field> *allFields) {
+ Field newParent = parentField;
+ Field* leaf = getSingleLeaf(&newParent);
+ leaf->set_field(matcher.field());
+ if (matcher.child_size() == 0) {
+ allFields->push_back(newParent);
+ return;
+ }
+ for (int i = 0; i < matcher.child_size(); ++i) {
+ leaf->add_child();
+ getFieldsFromFieldMatcher(matcher.child(i), newParent, allFields);
+ }
+}
+
+void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) {
+ Field parentField;
+ getFieldsFromFieldMatcher(matcher, parentField, allFields);
+}
+
+void flattenValueLeaves(const DimensionsValue& value,
+ std::vector<DimensionsValue> *allLaves) {
+ switch (value.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::kValueInt:
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ allLaves->push_back(value);
+ break;
+ case DimensionsValue::ValueCase::kValueTuple:
+ for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+ flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves);
+ }
+ break;
+ }
+}
+
+std::vector<HashableDimensionKey> getDimensionKeysForCondition(
+ const LogEvent& event, const MetricConditionLink& link) {
+ std::vector<Field> whatFields;
+ getFieldsFromFieldMatcher(link.dimensions_in_what(), &whatFields);
+ std::vector<Field> conditionFields;
+ getFieldsFromFieldMatcher(link.dimensions_in_condition(), &conditionFields);
+
+ std::vector<HashableDimensionKey> hashableDimensionKeys;
+
+ // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and
+ // directly construct the full condition value tree.
+ std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.dimensions_in_what());
+
+ for (size_t i = 0; i < whatValues.size(); ++i) {
+ std::vector<DimensionsValue> whatLeaves;
+ flattenValueLeaves(whatValues[i], &whatLeaves);
+ if (whatLeaves.size() != whatFields.size() ||
+ whatLeaves.size() != conditionFields.size()) {
+ ALOGE("Dimensions between what and condition not equal.");
+ return hashableDimensionKeys;
+ }
+ FieldValueMap conditionValueMap;
+ for (size_t j = 0; j < whatLeaves.size(); ++j) {
+ if (!setFieldInLeafValueProto(conditionFields[j], &whatLeaves[j])) {
+ ALOGE("Not able to reset the field for condition leaf value.");
+ return hashableDimensionKeys;
+ }
+ conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j]));
+ }
+ std::vector<DimensionsValue> conditionValues;
+ findDimensionsValues(conditionValueMap, link.dimensions_in_condition(), &conditionValues);
+ if (conditionValues.size() != 1) {
+ ALOGE("Not able to find unambiguous field value in condition atom.");
+ continue;
+ }
+ hashableDimensionKeys.push_back(HashableDimensionKey(conditionValues[0]));
}
- vector<KeyValuePair> dimensionKey = getDimensionKey(event, eventKey);
-
- for (int i = 0; i < link.key_in_what_size(); i++) {
- auto& kv = dimensionKey[i];
- kv.set_key(link.key_in_condition(i).key());
- }
-
- return HashableDimensionKey(dimensionKey);
+ return hashableDimensionKeys;
}
} // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 934c207..598027b 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -32,12 +32,16 @@
kTrue = 1,
};
+ConditionState operator|(ConditionState l, ConditionState r);
+void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored);
+void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored);
+
ConditionState evaluateCombinationCondition(const std::vector<int>& children,
const LogicalOperation& operation,
const std::vector<ConditionState>& conditionCache);
-HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
- const MetricConditionLink& link);
+std::vector<HashableDimensionKey> getDimensionKeysForCondition(
+ const LogEvent& event, const MetricConditionLink& link);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index cb3f3d6..addc111 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -209,7 +209,7 @@
int WAKE_LOCK_ACQUIRE_VALUE = 1;
int WAKE_LOCK_RELEASE_VALUE = 0;
- int APP_USAGE_ID = 12345;
+ int APP_USAGE_TAG_ID = 12345;
int APP_USAGE_UID_KEY_ID = 1;
int APP_USAGE_STATE_KEY = 2;
int APP_USAGE_FOREGROUND = 1;
@@ -259,8 +259,9 @@
metric->set_name("METRIC_2");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- KeyMatcher* keyMatcher = metric->add_dimension();
- keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+ FieldMatcher* dimensions = metric->mutable_dimensions();
+ dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
+ dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
// Anomaly threshold for background count.
// TODO(b/70627390): Uncomment once the bug is fixed.
@@ -280,8 +281,10 @@
metric->set_name("METRIC_3");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- keyMatcher = metric->add_dimension();
- keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+
+ dimensions = metric->mutable_dimensions();
+ dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
+ dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
metric->set_condition("SCREEN_IS_OFF");
// Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
@@ -289,41 +292,52 @@
metric->set_name("METRIC_4");
metric->set_what("APP_GET_WL");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- keyMatcher = metric->add_dimension();
- keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ dimensions = metric->mutable_dimensions();
+ dimensions->set_field(WAKE_LOCK_TAG_ID);
+ dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+
+
metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
MetricConditionLink* link = metric->add_links();
link->set_condition("APP_IS_BACKGROUND");
- link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
- link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+ link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+ link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+ link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
// Duration of an app holding any wl, while screen on and app in background, slice by uid
DurationMetric* durationMetric = config.add_duration_metric();
durationMetric->set_name("METRIC_5");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
- keyMatcher = durationMetric->add_dimension();
- keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ dimensions = durationMetric->mutable_dimensions();
+ dimensions->set_field(WAKE_LOCK_TAG_ID);
+ dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
- link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
- link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+ link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+ link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+ link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
// max Duration of an app holding any wl, while screen on and app in background, slice by uid
durationMetric = config.add_duration_metric();
durationMetric->set_name("METRIC_6");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
- keyMatcher = durationMetric->add_dimension();
- keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+ dimensions = durationMetric->mutable_dimensions();
+ dimensions->set_field(WAKE_LOCK_TAG_ID);
+ dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
- link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
- link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+ link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+ link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+ link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
// Duration of an app holding any wl, while screen on and app in background
durationMetric = config.add_duration_metric();
@@ -334,8 +348,11 @@
durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
- link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
- link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+ link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+ link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+ link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+ link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+
// Duration of screen on time.
durationMetric = config.add_duration_metric();
@@ -362,8 +379,9 @@
valueMetric->set_what("KERNEL_WAKELOCK");
valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY);
valueMetric->set_condition("SCREEN_IS_ON");
- keyMatcher = valueMetric->add_dimension();
- keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
+ dimensions = valueMetric->mutable_dimensions();
+ dimensions->set_field(KERNEL_WAKELOCK_TAG_ID);
+ dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY);
// This is for testing easier. We should never set bucket size this small.
valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
@@ -376,73 +394,75 @@
GaugeMetric* gaugeMetric = config.add_gauge_metric();
gaugeMetric->set_name("METRIC_10");
gaugeMetric->set_what("DEVICE_TEMPERATURE");
- gaugeMetric->mutable_gauge_fields()->add_field_num(DEVICE_TEMPERATURE_KEY);
+ auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID);
+ gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY);
gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
- // Event matchers............
+ // Event matchers.
AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher();
temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE");
- temperatureAtomMatcher->mutable_simple_atom_matcher()->set_tag(
+ temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id(
DEVICE_TEMPERATURE_TAG_ID);
AtomMatcher* eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("SCREEN_TURNED_ON");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
- KeyValueMatcher* keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
- keyValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
+ simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
+ FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
+ fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("SCREEN_TURNED_OFF");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
- keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
- keyValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
+ simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
+ fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
+ fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("PROCESS_STATE_CHANGE");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(UID_PROCESS_STATE_TAG_ID);
+ simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("APP_GOES_BACKGROUND");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(APP_USAGE_ID);
- keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
- keyValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
+ simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
+ fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
+ fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("APP_GOES_FOREGROUND");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(APP_USAGE_ID);
- keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
- keyValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
+ simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
+ fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
+ fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("APP_GET_WL");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
- keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
- keyValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
+ simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
+ fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
+ fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("APP_RELEASE_WL");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
- keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
- keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
- keyValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
+ simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
+ fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+ fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
+ fieldValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
// pulled events
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("KERNEL_WAKELOCK");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
+ simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID);
// Predicates.............
Predicate* predicate = config.add_predicate();
@@ -464,8 +484,9 @@
simplePredicate = predicate->mutable_simple_predicate();
simplePredicate->set_start("APP_GOES_BACKGROUND");
simplePredicate->set_stop("APP_GOES_FOREGROUND");
- KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension();
- predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+ FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions();
+ predicate_dimension1->set_field(APP_USAGE_TAG_ID);
+ predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID);
simplePredicate->set_count_nesting(false);
predicate = config.add_predicate();
@@ -480,10 +501,10 @@
simplePredicate = predicate->mutable_simple_predicate();
simplePredicate->set_start("APP_GET_WL");
simplePredicate->set_stop("APP_RELEASE_WL");
- KeyMatcher* predicate_dimension = simplePredicate->add_dimension();
- predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
- predicate_dimension = simplePredicate->add_dimension();
- predicate_dimension->set_key(WAKE_LOCK_NAME_KEY);
+ FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions();
+ predicate_dimension1->set_field(WAKE_LOCK_TAG_ID);
+ predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+ predicate_dimension->add_child()->set_field(WAKE_LOCK_NAME_KEY);
simplePredicate->set_count_nesting(true);
predicate = config.add_predicate();
@@ -492,8 +513,9 @@
simplePredicate->set_start("APP_GET_WL");
simplePredicate->set_stop("APP_RELEASE_WL");
simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
- predicate_dimension = simplePredicate->add_dimension();
- predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+ predicate_dimension = simplePredicate->mutable_dimensions();
+ predicate_dimension->set_field(WAKE_LOCK_TAG_ID);
+ predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
simplePredicate->set_count_nesting(true);
return config;
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
new file mode 100644
index 0000000..886a33bf
--- /dev/null
+++ b/cmds/statsd/src/dimension.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "dimension.h"
+#include "field_util.h"
+
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) {
+ if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) {
+ return getSingleLeafValue(&value->value_tuple().dimensions_value(0));
+ } else {
+ return value;
+ }
+}
+
+DimensionsValue getSingleLeafValue(const DimensionsValue& value) {
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ return *leafValue;
+}
+
+void appendLeafNodeToParent(const Field& field,
+ const DimensionsValue& value,
+ DimensionsValue* parentValue) {
+ if (field.child_size() <= 0) {
+ *parentValue = value;
+ parentValue->set_field(field.field());
+ return;
+ }
+ parentValue->set_field(field.field());
+ int idx = -1;
+ for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) {
+ if (parentValue->mutable_value_tuple()->dimensions_value(i).field() ==
+ field.child(0).field()) {
+ idx = i;
+ }
+ }
+ if (idx < 0) {
+ parentValue->mutable_value_tuple()->add_dimensions_value();
+ idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1;
+ }
+ appendLeafNodeToParent(
+ field.child(0), value,
+ parentValue->mutable_value_tuple()->mutable_dimensions_value(idx));
+}
+
+void addNodeToRootDimensionsValues(const Field& field,
+ const DimensionsValue& node,
+ std::vector<DimensionsValue>* rootValues) {
+ if (rootValues == nullptr) {
+ return;
+ }
+ if (rootValues->empty()) {
+ DimensionsValue rootValue;
+ appendLeafNodeToParent(field, node, &rootValue);
+ rootValues->push_back(rootValue);
+ } else {
+ for (size_t i = 0; i < rootValues->size(); ++i) {
+ appendLeafNodeToParent(field, node, &rootValues->at(i));
+ }
+ }
+}
+
+namespace {
+
+void findDimensionsValues(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<DimensionsValue>* rootDimensionsValues);
+
+void findNonRepeatedDimensionsValues(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<DimensionsValue>* rootValues) {
+ if (matcher.child_size() > 0) {
+ for (const auto& childMatcher : matcher.child()) {
+ Field childField = field;
+ appendLeaf(&childField, childMatcher.field());
+ findDimensionsValues(fieldValueMap, childMatcher, childField, rootValues);
+ }
+ } else {
+ auto ret = fieldValueMap.equal_range(field);
+ int found = 0;
+ for (auto it = ret.first; it != ret.second; ++it) {
+ found++;
+ }
+ // Not found.
+ if (found <= 0) {
+ return;
+ }
+ if (found > 1) {
+ ALOGE("Found multiple values for optional field.");
+ return;
+ }
+ addNodeToRootDimensionsValues(field, ret.first->second, rootValues);
+ }
+}
+
+void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<DimensionsValue>* rootValues) {
+ if (matcher.position() == Position::FIRST) {
+ Field first_field = field;
+ setPositionForLeaf(&first_field, 0);
+ findNonRepeatedDimensionsValues(fieldValueMap, matcher, first_field, rootValues);
+ } else {
+ auto itLower = fieldValueMap.lower_bound(field);
+ if (itLower == fieldValueMap.end()) {
+ return;
+ }
+ Field next_field = field;
+ getNextField(&next_field);
+ auto itUpper = fieldValueMap.lower_bound(next_field);
+
+ switch (matcher.position()) {
+ case Position::LAST:
+ {
+ itUpper--;
+ if (itUpper != fieldValueMap.end()) {
+ Field last_field = field;
+ int last_index = getPositionByReferenceField(field, itUpper->first);
+ if (last_index < 0) {
+ return;
+ }
+ setPositionForLeaf(&last_field, last_index);
+ findNonRepeatedDimensionsValues(
+ fieldValueMap, matcher, last_field, rootValues);
+ }
+ }
+ break;
+ case Position::ANY:
+ {
+ std::set<int> indexes;
+ for (auto it = itLower; it != itUpper; ++it) {
+ int index = getPositionByReferenceField(field, it->first);
+ if (index >= 0) {
+ indexes.insert(index);
+ }
+ }
+ if (!indexes.empty()) {
+ Field any_field = field;
+ std::vector<DimensionsValue> allValues;
+ for (const int index : indexes) {
+ setPositionForLeaf(&any_field, index);
+ std::vector<DimensionsValue> newValues = *rootValues;
+ findNonRepeatedDimensionsValues(
+ fieldValueMap, matcher, any_field, &newValues);
+ allValues.insert(allValues.end(), newValues.begin(), newValues.end());
+ }
+ rootValues->clear();
+ rootValues->insert(rootValues->end(), allValues.begin(), allValues.end());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void findDimensionsValues(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<DimensionsValue>* rootDimensionsValues) {
+ if (!matcher.has_position()) {
+ findNonRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+ } else {
+ findRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+ }
+}
+
+} // namespace
+
+void findDimensionsValues(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ std::vector<DimensionsValue>* rootDimensionsValues) {
+ findDimensionsValues(fieldValueMap, matcher,
+ buildSimpleAtomField(matcher.field()), rootDimensionsValues);
+}
+
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId) {
+ FieldMatcher matcher;
+ matcher.set_field(tagId);
+ return matcher;
+}
+
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum) {
+ FieldMatcher matcher;
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(atomFieldNum);
+ return matcher;
+}
+
+constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1;
+constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1;
+constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2;
+
+void buildAttributionUidFieldMatcher(const int tagId, const Position position,
+ FieldMatcher *matcher) {
+ matcher->set_field(tagId);
+ matcher->add_child()->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+ FieldMatcher* child = matcher->mutable_child(0)->add_child();
+ child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void buildAttributionTagFieldMatcher(const int tagId, const Position position,
+ FieldMatcher *matcher) {
+ matcher->set_field(tagId);
+ FieldMatcher* child = matcher->add_child();
+ child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+ child->set_position(position);
+ child = child->add_child();
+ child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void buildAttributionFieldMatcher(const int tagId, const Position position,
+ FieldMatcher *matcher) {
+ matcher->set_field(tagId);
+ FieldMatcher* child = matcher->add_child();
+ child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+ child->set_position(position);
+ child = child->add_child();
+ child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+ child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+ *flattened += std::to_string(value.field());
+ *flattened += ":";
+ switch (value.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ *flattened += value.value_str();
+ break;
+ case DimensionsValue::ValueCase::kValueInt:
+ *flattened += std::to_string(value.value_int());
+ break;
+ case DimensionsValue::ValueCase::kValueLong:
+ *flattened += std::to_string(value.value_long());
+ break;
+ case DimensionsValue::ValueCase::kValueBool:
+ *flattened += std::to_string(value.value_bool());
+ break;
+ case DimensionsValue::ValueCase::kValueFloat:
+ *flattened += std::to_string(value.value_float());
+ break;
+ case DimensionsValue::ValueCase::kValueTuple:
+ {
+ *flattened += "{";
+ for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+ DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened);
+ *flattened += "|";
+ }
+ *flattened += "}";
+ }
+ break;
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ break;
+ }
+}
+
+void getDimensionsValueLeafNodes(
+ const DimensionsValue& value, std::vector<DimensionsValue> *leafNodes) {
+ switch (value.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::kValueInt:
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ leafNodes->push_back(value);
+ break;
+ case DimensionsValue::ValueCase::kValueTuple:
+ for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+ getDimensionsValueLeafNodes(value.value_tuple().dimensions_value(i), leafNodes);
+ }
+ break;
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ break;
+ default:
+ break;
+ }
+}
+
+std::string DimensionsValueToString(const DimensionsValue& value) {
+ std::string flatten;
+ DimensionsValueToString(value, &flatten);
+ return flatten;
+}
+
+bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) {
+ if (dimension.field() != sub.field()) {
+ return false;
+ }
+ if (dimension.value_case() != sub.value_case()) {
+ return false;
+ }
+ switch (dimension.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ return dimension.value_str() == sub.value_str();
+ case DimensionsValue::ValueCase::kValueInt:
+ return dimension.value_int() == sub.value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ return dimension.value_long() == sub.value_long();
+ case DimensionsValue::ValueCase::kValueBool:
+ return dimension.value_bool() == sub.value_bool();
+ case DimensionsValue::ValueCase::kValueFloat:
+ return dimension.value_float() == sub.value_float();
+ case DimensionsValue::ValueCase::kValueTuple: {
+ if (dimension.value_tuple().dimensions_value_size() < sub.value_tuple().dimensions_value_size()) {
+ return false;
+ }
+ bool allSub = true;
+ for (int i = 0; i < sub.value_tuple().dimensions_value_size(); ++i) {
+ bool isSub = false;
+ for (int j = 0; !isSub && j < dimension.value_tuple().dimensions_value_size(); ++j) {
+ isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j),
+ sub.value_tuple().dimensions_value(i));
+ }
+ allSub &= isSub;
+ }
+ return allSub;
+ }
+ break;
+ case DimensionsValue::ValueCase::VALUE_NOT_SET:
+ return false;
+ default:
+ return false;
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
new file mode 100644
index 0000000..c866958
--- /dev/null
+++ b/cmds/statsd/src/dimension.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <log/logprint.h>
+#include <set>
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "field_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+
+// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one
+// leaf node at most.
+const DimensionsValue* getSingleLeafValue(const DimensionsValue* value);
+DimensionsValue getSingleLeafValue(const DimensionsValue& value);
+
+// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
+// represents a tree. When the input proto has repeated fields and the input "dimensions" wants
+// "ANY" locations, it will return multiple trees.
+void findDimensionsValues(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ std::vector<DimensionsValue>* rootDimensionsValues);
+
+// Utils to build FieldMatcher proto for simple one-depth atoms.
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum);
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId);
+
+// Utils to build FieldMatcher proto for attribution nodes.
+FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position);
+FieldMatcher buildAttributionTagFieldMatcher(const int tagId, const Position position);
+FieldMatcher buildAttributionFieldMatcher(const int tagId, const Position position);
+
+// Utils to print pretty string for DimensionsValue proto.
+std::string DimensionsValueToString(const DimensionsValue& value);
+void DimensionsValueToString(const DimensionsValue& value, std::string *flattened);
+
+bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp
new file mode 100644
index 0000000..d10e167
--- /dev/null
+++ b/cmds/statsd/src/field_util.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+#include "field_util.h"
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// This function is to compare two Field trees where each node has at most one child.
+bool CompareField(const Field& a, const Field& b) {
+ if (a.field() < b.field()) {
+ return true;
+ }
+ if (a.field() > b.field()) {
+ return false;
+ }
+ if (a.position_index() < b.position_index()) {
+ return true;
+ }
+ if (a.position_index() > b.position_index()) {
+ return false;
+ }
+ if (a.child_size() < b.child_size()) {
+ return true;
+ }
+ if (a.child_size() > b.child_size()) {
+ return false;
+ }
+ if (a.child_size() == 0 && b.child_size() == 0) {
+ return false;
+ }
+ return CompareField(a.child(0), b.child(0));
+}
+
+const Field* getSingleLeaf(const Field* field) {
+ if (field->child_size() <= 0) {
+ return field;
+ } else {
+ return getSingleLeaf(&field->child(0));
+ }
+}
+
+Field* getSingleLeaf(Field* field) {
+ if (field->child_size() <= 0) {
+ return field;
+ } else {
+ return getSingleLeaf(field->mutable_child(0));
+ }
+}
+
+void FieldToString(const Field& field, std::string *flattened) {
+ *flattened += std::to_string(field.field());
+ if (field.has_position_index()) {
+ *flattened += "[";
+ *flattened += std::to_string(field.position_index());
+ *flattened += "]";
+ }
+ if (field.child_size() <= 0) {
+ return;
+ }
+ *flattened += ".";
+ *flattened += "{";
+ for (int i = 0 ; i < field.child_size(); ++i) {
+ *flattened += FieldToString(field.child(i));
+ }
+ *flattened += "},";
+}
+
+std::string FieldToString(const Field& field) {
+ std::string flatten;
+ FieldToString(field, &flatten);
+ return flatten;
+}
+
+bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) {
+ if (field.child_size() <= 0) {
+ leafValue->set_field(field.field());
+ return true;
+ } else if (field.child_size() == 1) {
+ return setFieldInLeafValueProto(field.child(0), leafValue);
+ } else {
+ ALOGE("Not able to set the 'field' in leaf value for multiple children.");
+ return false;
+ }
+}
+
+Field buildAtomField(const int tagId, const Field &atomField) {
+ Field field;
+ *field.add_child() = atomField;
+ field.set_field(tagId);
+ return field;
+}
+
+Field buildSimpleAtomField(const int tagId, const int atomFieldNum) {
+ Field field;
+ field.set_field(tagId);
+ field.add_child()->set_field(atomFieldNum);
+ return field;
+}
+
+Field buildSimpleAtomField(const int tagId) {
+ Field field;
+ field.set_field(tagId);
+ return field;
+}
+
+void appendLeaf(Field *parent, int node_field_num) {
+ if (!parent->has_field()) {
+ parent->set_field(node_field_num);
+ } else if (parent->child_size() <= 0) {
+ parent->add_child()->set_field(node_field_num);
+ } else {
+ appendLeaf(parent->mutable_child(0), node_field_num);
+ }
+}
+
+void appendLeaf(Field *parent, int node_field_num, int position) {
+ if (!parent->has_field()) {
+ parent->set_field(node_field_num);
+ parent->set_position_index(position);
+ } else if (parent->child_size() <= 0) {
+ auto child = parent->add_child();
+ child->set_field(node_field_num);
+ child->set_position_index(position);
+ } else {
+ appendLeaf(parent->mutable_child(0), node_field_num, position);
+ }
+}
+
+
+void getNextField(Field* field) {
+ if (field->child_size() <= 0) {
+ field->set_field(field->field() + 1);
+ return;
+ }
+ if (field->child_size() != 1) {
+ return;
+ }
+ getNextField(field->mutable_child(0));
+}
+
+void increasePosition(Field *field) {
+ if (!field->has_position_index()) {
+ field->set_position_index(0);
+ } else {
+ field->set_position_index(field->position_index() + 1);
+ }
+}
+
+int getPositionByReferenceField(const Field& ref, const Field& field_with_index) {
+ if (ref.child_size() <= 0) {
+ return field_with_index.position_index();
+ }
+ if (ref.child_size() != 1 ||
+ field_with_index.child_size() != 1) {
+ return -1;
+ }
+ return getPositionByReferenceField(ref.child(0), field_with_index.child(0));
+}
+
+void setPositionForLeaf(Field *field, int index) {
+ if (field->child_size() <= 0) {
+ field->set_position_index(index);
+ } else {
+ setPositionForLeaf(field->mutable_child(0), index);
+ }
+}
+
+void findFields(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<Field>* rootFields);
+
+void findNonRepeatedFields(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<Field>* rootFields) {
+ if (matcher.child_size() > 0) {
+ for (const auto& childMatcher : matcher.child()) {
+ Field childField = field;
+ appendLeaf(&childField, childMatcher.field());
+ findFields(fieldValueMap, childMatcher, childField, rootFields);
+ }
+ } else {
+ auto ret = fieldValueMap.equal_range(field);
+ int found = 0;
+ for (auto it = ret.first; it != ret.second; ++it) {
+ found++;
+ }
+ // Not found.
+ if (found <= 0) {
+ return;
+ }
+ if (found > 1) {
+ ALOGE("Found multiple values for optional field.");
+ return;
+ }
+ rootFields->push_back(ret.first->first);
+ }
+}
+
+void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
+ const Field& field, std::vector<Field>* rootFields) {
+ if (matcher.position() == Position::FIRST) {
+ Field first_field = field;
+ setPositionForLeaf(&first_field, 0);
+ findNonRepeatedFields(fieldValueMap, matcher, first_field, rootFields);
+ } else {
+ auto itLower = fieldValueMap.lower_bound(field);
+ if (itLower == fieldValueMap.end()) {
+ return;
+ }
+ Field next_field = field;
+ getNextField(&next_field);
+ auto itUpper = fieldValueMap.lower_bound(next_field);
+
+ switch (matcher.position()) {
+ case Position::LAST:
+ {
+ itUpper--;
+ if (itUpper != fieldValueMap.end()) {
+ Field last_field = field;
+ int last_index = getPositionByReferenceField(field, itUpper->first);
+ if (last_index < 0) {
+ return;
+ }
+ setPositionForLeaf(&last_field, last_index);
+ findNonRepeatedFields(
+ fieldValueMap, matcher, last_field, rootFields);
+ }
+ }
+ break;
+ case Position::ANY:
+ {
+ std::set<int> indexes;
+ for (auto it = itLower; it != itUpper; ++it) {
+ int index = getPositionByReferenceField(field, it->first);
+ if (index >= 0) {
+ indexes.insert(index);
+ }
+ }
+ if (!indexes.empty()) {
+ Field any_field = field;
+ for (const int index : indexes) {
+ setPositionForLeaf(&any_field, index);
+ findNonRepeatedFields(
+ fieldValueMap, matcher, any_field, rootFields);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void findFields(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ const Field& field,
+ std::vector<Field>* rootFields) {
+ if (!matcher.has_position()) {
+ findNonRepeatedFields(fieldValueMap, matcher, field, rootFields);
+ } else {
+ findRepeatedFields(fieldValueMap, matcher, field, rootFields);
+ }
+}
+
+void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) {
+ std::vector<Field> rootFields;
+ findFields(*fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), &rootFields);
+ std::set<Field, FieldCmp> rootFieldSet(rootFields.begin(), rootFields.end());
+ auto it = fieldValueMap->begin();
+ while (it != fieldValueMap->end()) {
+ if (rootFieldSet.find(it->first) == rootFieldSet.end()) {
+ it = fieldValueMap->erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+bool hasLeafNode(const FieldMatcher& matcher) {
+ if (!matcher.has_field()) {
+ return false;
+ }
+ for (int i = 0; i < matcher.child_size(); ++i) {
+ if (hasLeafNode(matcher.child(i))) {
+ return true;
+ }
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h
new file mode 100644
index 0000000..5907e17
--- /dev/null
+++ b/cmds/statsd/src/field_util.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Function to sort the Field protos.
+bool CompareField(const Field& a, const Field& b);
+struct FieldCmp {
+ bool operator()(const Field& a, const Field& b) const {
+ return CompareField(a, b);
+ }
+};
+
+// Flattened dimensions value map. To save space, usually the key contains the tree structure info
+// and value field is only leaf node.
+typedef std::map<Field, DimensionsValue, FieldCmp> FieldValueMap;
+
+// Util function to print the Field proto.
+std::string FieldToString(const Field& field);
+
+// Util function to find the leaf node from the input Field proto and set it in the corresponding
+// value proto.
+bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue);
+
+// Returns the leaf node from the Field proto. It assume that the input has only one
+// leaf node at most.
+const Field* getSingleLeaf(const Field* field);
+Field* getSingleLeaf(Field* field);
+
+// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most.
+void appendLeaf(Field *parent, int node_field_num);
+void appendLeaf(Field *parent, int node_field_num, int position);
+
+// Given the field sorting logic, this function is to increase the "field" at the leaf node.
+void getNextField(Field* field);
+
+// Increase the position index for the node. If the "position_index" is not set, set it as 0.
+void increasePosition(Field *field);
+
+// Finds the leaf node and set the index there.
+void setPositionForLeaf(Field *field, int index);
+
+// Returns true if the matcher has specified at least one leaf node.
+bool hasLeafNode(const FieldMatcher& matcher);
+
+// The two input Field proto are describing the same tree structure. Both contain one leaf node at
+// most. This is find the position index info for the leaf node at "reference" stored in the
+// "field_with_index" tree.
+int getPositionByReferenceField(const Field& reference, const Field& field_with_index);
+
+// Utils to build the Field proto for simple atom fields.
+Field buildAtomField(const int tagId, const Field &atomField);
+Field buildSimpleAtomField(const int tagId, const int atomFieldNum);
+Field buildSimpleAtomField(const int tagId);
+
+// Find out all the fields specified by the matcher.
+void findFields(
+ const FieldValueMap& fieldValueMap,
+ const FieldMatcher& matcher,
+ std::vector<Field>* rootFields);
+
+// Filter out the fields not in the field matcher.
+void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index d660b5f..49a6e33 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,8 +17,14 @@
#define DEBUG true // STOPSHIP if true
#include "logd/LogEvent.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <set>
#include <sstream>
-#include "stats_util.h"
+
+#include "field_util.h"
+#include "dimension.h"
+#include "stats_log_util.h"
namespace android {
namespace os {
@@ -30,16 +36,20 @@
using android::util::ProtoOutputStream;
LogEvent::LogEvent(log_msg& msg) {
- mContext =
+ android_log_context context =
create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
mLogUid = msg.entry_v4.uid;
- init(mContext);
+ init(context);
+ if (context) {
+ android_log_destroy(&context);
+ }
}
LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
mTimestampNs = timestampNs;
mTagId = tagId;
+ mLogUid = 0;
mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
if (mContext) {
android_log_write_int32(mContext, tagId);
@@ -53,6 +63,14 @@
// turns to reader mode
mContext = create_android_log_parser(buffer, len);
init(mContext);
+ // destroy the context to save memory.
+ android_log_destroy(&mContext);
+ }
+}
+
+LogEvent::~LogEvent() {
+ if (mContext) {
+ android_log_destroy(&mContext);
}
}
@@ -98,19 +116,72 @@
return false;
}
-LogEvent::~LogEvent() {
+bool LogEvent::write(const std::vector<AttributionNode>& nodes) {
if (mContext) {
- android_log_destroy(&mContext);
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ if (!write(nodes[i])) {
+ return false;
+ }
+ }
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LogEvent::write(const AttributionNode& node) {
+ if (mContext) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ if (android_log_write_int32(mContext, node.uid()) < 0) {
+ return false;
+ }
+ if (android_log_write_string8(mContext, node.tag().c_str()) < 0) {
+ return false;
+ }
+ if (android_log_write_int32(mContext, node.uid()) < 0) {
+ return false;
+ }
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+void increaseField(Field *field, bool is_child) {
+ if (is_child) {
+ if (field->child_size() <= 0) {
+ field->add_child();
+ }
+ } else {
+ field->clear_child();
+ }
+ Field* curr = is_child ? field->mutable_child(0) : field;
+ if (!curr->has_field()) {
+ curr->set_field(1);
+ } else {
+ curr->set_field(curr->field() + 1);
}
}
+} // namespace
+
/**
* The elements of each log event are stored as a vector of android_log_list_elements.
* The goal is to do as little preprocessing as possible, because we read a tiny fraction
* of the elements that are written to the log.
*/
void LogEvent::init(android_log_context context) {
- mElements.clear();
android_log_list_element elem;
// TODO: The log is actually structured inside one list. This is convenient
// because we'll be able to use it to put the attribution (WorkSource) block first
@@ -118,24 +189,79 @@
// list-related log elements and the order we get there is our index-keyed data
// structure.
int i = 0;
+
+ int seenListStart = 0;
+
+ Field field;
do {
elem = android_log_read_next(context);
switch ((int)elem.type) {
case EVENT_TYPE_INT:
- // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would
- // be the list starting at [2].
+ // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id.
if (i == 1) {
mTagId = elem.data.int32;
- break;
+ } else {
+ increaseField(&field, seenListStart > 0/* is_child */);
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_value_int(elem.data.int32);
+ setFieldInLeafValueProto(field, &dimensionsValue);
+ mFieldValueMap.insert(
+ std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
}
+ break;
case EVENT_TYPE_FLOAT:
+ {
+ increaseField(&field, seenListStart > 0/* is_child */);
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_value_float(elem.data.float32);
+ setFieldInLeafValueProto(field, &dimensionsValue);
+ mFieldValueMap.insert(
+ std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+ }
+ break;
case EVENT_TYPE_STRING:
+ {
+ increaseField(&field, seenListStart > 0/* is_child */);
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_value_str(string(elem.data.string, elem.len).c_str());
+ setFieldInLeafValueProto(field, &dimensionsValue);
+ mFieldValueMap.insert(
+ std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+ }
+ break;
case EVENT_TYPE_LONG:
- mElements.push_back(elem);
+ {
+ increaseField(&field, seenListStart > 0 /* is_child */);
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_value_long(elem.data.int64);
+ setFieldInLeafValueProto(field, &dimensionsValue);
+ mFieldValueMap.insert(
+ std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+ }
break;
case EVENT_TYPE_LIST:
+ if (i >= 1) {
+ if (seenListStart > 0) {
+ increasePosition(&field);
+ } else {
+ increaseField(&field, false /* is_child */);
+ }
+ seenListStart++;
+ if (seenListStart >= 3) {
+ ALOGE("Depth > 2. Not supported!");
+ return;
+ }
+ }
break;
case EVENT_TYPE_LIST_STOP:
+ seenListStart--;
+ if (seenListStart == 0) {
+ field.clear_position_index();
+ } else {
+ if (field.child_size() > 0) {
+ field.mutable_child(0)->clear_field();
+ }
+ }
break;
case EVENT_TYPE_UNKNOWN:
break;
@@ -147,142 +273,145 @@
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
- if (key < 1 || (key - 1) >= mElements.size()) {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
*err = BAD_INDEX;
return 0;
}
- key--;
- const android_log_list_element& elem = mElements[key];
- if (elem.type == EVENT_TYPE_INT) {
- return elem.data.int32;
- } else if (elem.type == EVENT_TYPE_LONG) {
- return elem.data.int64;
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- return (int64_t)elem.data.float32;
- } else {
- *err = BAD_TYPE;
- return 0;
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return (int64_t)leafValue->value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ return leafValue->value_long();
+ case DimensionsValue::ValueCase::kValueBool:
+ return leafValue->value_bool() ? 1 : 0;
+ case DimensionsValue::ValueCase::kValueFloat:
+ return (int64_t)leafValue->value_float();
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
}
}
const char* LogEvent::GetString(size_t key, status_t* err) const {
- if (key < 1 || (key - 1) >= mElements.size()) {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
*err = BAD_INDEX;
- return NULL;
+ return 0;
}
- key--;
- const android_log_list_element& elem = mElements[key];
- if (elem.type != EVENT_TYPE_STRING) {
- *err = BAD_TYPE;
- return NULL;
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ return leafValue->value_str().c_str();
+ case DimensionsValue::ValueCase::kValueInt:
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
}
- // Need to add the '/0' at the end by specifying the length of the string.
- return string(elem.data.string, elem.len).c_str();
}
bool LogEvent::GetBool(size_t key, status_t* err) const {
- if (key < 1 || (key - 1) >= mElements.size()) {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
*err = BAD_INDEX;
return 0;
}
- key--;
- const android_log_list_element& elem = mElements[key];
- if (elem.type == EVENT_TYPE_INT) {
- return elem.data.int32 != 0;
- } else if (elem.type == EVENT_TYPE_LONG) {
- return elem.data.int64 != 0;
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- return elem.data.float32 != 0;
- } else {
- *err = BAD_TYPE;
- return 0;
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return leafValue->value_int() != 0;
+ case DimensionsValue::ValueCase::kValueLong:
+ return leafValue->value_long() != 0;
+ case DimensionsValue::ValueCase::kValueBool:
+ return leafValue->value_bool();
+ case DimensionsValue::ValueCase::kValueFloat:
+ return leafValue->value_float() != 0;
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
}
}
float LogEvent::GetFloat(size_t key, status_t* err) const {
- if (key < 1 || (key - 1) >= mElements.size()) {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
*err = BAD_INDEX;
return 0;
}
- key--;
- const android_log_list_element& elem = mElements[key];
- if (elem.type == EVENT_TYPE_INT) {
- return (float)elem.data.int32;
- } else if (elem.type == EVENT_TYPE_LONG) {
- return (float)elem.data.int64;
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- return elem.data.float32;
- } else {
- *err = BAD_TYPE;
- return 0;
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return (float)leafValue->value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ return (float)leafValue->value_long();
+ case DimensionsValue::ValueCase::kValueBool:
+ return leafValue->value_bool() ? 1.0f : 0.0f;
+ case DimensionsValue::ValueCase::kValueFloat:
+ return leafValue->value_float();
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
}
}
-KeyValuePair LogEvent::GetKeyValueProto(size_t key) const {
- KeyValuePair pair;
- pair.set_key(key);
- // If the value is not valid, return the KeyValuePair without assigning the value.
- // Caller can detect the error by checking the enum for "one of" proto type.
- if (key < 1 || (key - 1) >= mElements.size()) {
- return pair;
- }
- key--;
+void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher,
+ std::vector<DimensionsValue> *dimensionsValues) const {
+ findDimensionsValues(mFieldValueMap, matcher, dimensionsValues);
+}
- const android_log_list_element& elem = mElements[key];
- if (elem.type == EVENT_TYPE_INT) {
- pair.set_value_int(elem.data.int32);
- } else if (elem.type == EVENT_TYPE_LONG) {
- pair.set_value_long(elem.data.int64);
- } else if (elem.type == EVENT_TYPE_STRING) {
- pair.set_value_str(elem.data.string);
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- pair.set_value_float(elem.data.float32);
+bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher,
+ DimensionsValue* dimensionsValue) const {
+ std::vector<DimensionsValue> rootDimensionsValues;
+ findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues);
+ if (rootDimensionsValues.size() != 1) {
+ return false;
}
- return pair;
+ *dimensionsValue = rootDimensionsValues.front();
+ return true;
+}
+
+bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField,
+ DimensionsValue* dimensionsValue) const {
+ return GetAtomDimensionsValueProto(
+ buildSimpleAtomFieldMatcher(mTagId, atomField), dimensionsValue);
+}
+
+DimensionsValue LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField) const {
+ DimensionsValue dimensionsValue;
+ GetSimpleAtomDimensionsValueProto(atomField, &dimensionsValue);
+ return dimensionsValue;
}
string LogEvent::ToString() const {
ostringstream result;
result << "{ " << mTimestampNs << " (" << mTagId << ")";
- const size_t N = mElements.size();
- for (size_t i=0; i<N; i++) {
- result << " ";
- result << (i + 1);
+ for (const auto& itr : mFieldValueMap) {
+ result << FieldToString(itr.first);
result << "->";
- const android_log_list_element& elem = mElements[i];
- if (elem.type == EVENT_TYPE_INT) {
- result << elem.data.int32;
- } else if (elem.type == EVENT_TYPE_LONG) {
- result << elem.data.int64;
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- result << elem.data.float32;
- } else if (elem.type == EVENT_TYPE_STRING) {
- // Need to add the '/0' at the end by specifying the length of the string.
- result << string(elem.data.string, elem.len).c_str();
- }
+ result << DimensionsValueToString(itr.second);
+ result << " ";
}
result << " }";
return result.str();
}
-void LogEvent::ToProto(ProtoOutputStream& proto) const {
- long long atomToken = proto.start(FIELD_TYPE_MESSAGE | mTagId);
- const size_t N = mElements.size();
- for (size_t i=0; i<N; i++) {
- const int key = i + 1;
-
- const android_log_list_element& elem = mElements[i];
- if (elem.type == EVENT_TYPE_INT) {
- proto.write(FIELD_TYPE_INT32 | key, elem.data.int32);
- } else if (elem.type == EVENT_TYPE_LONG) {
- proto.write(FIELD_TYPE_INT64 | key, (long long)elem.data.int64);
- } else if (elem.type == EVENT_TYPE_FLOAT) {
- proto.write(FIELD_TYPE_FLOAT | key, elem.data.float32);
- } else if (elem.type == EVENT_TYPE_STRING) {
- proto.write(FIELD_TYPE_STRING | key, elem.data.string);
- }
- }
- proto.end(atomToken);
+void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
+ writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput);
}
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index d3f38de..8f3dedf 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -16,6 +16,7 @@
#pragma once
+#include "field_util.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include <android/util/ProtoOutputStream.h>
@@ -23,9 +24,11 @@
#include <log/log_read.h>
#include <private/android_logger.h>
#include <utils/Errors.h>
+#include <utils/JenkinsHash.h>
#include <memory>
#include <string>
+#include <map>
#include <vector>
namespace android {
@@ -77,6 +80,20 @@
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
+ /*
+ * Get DimensionsValue proto objects from FieldMatcher.
+ */
+ void GetAtomDimensionsValueProtos(
+ const FieldMatcher& matcher, std::vector<DimensionsValue> *dimensionsValues) const;
+ bool GetAtomDimensionsValueProto(
+ const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const;
+
+ /*
+ * Get a DimensionsValue proto objects from Field.
+ */
+ bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const;
+ DimensionsValue GetSimpleAtomDimensionsValueProto(size_t atomField) const;
+
/**
* Write test data to the LogEvent. This can only be used when the LogEvent is constructed
* using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
@@ -87,6 +104,8 @@
bool write(int64_t value);
bool write(const string& value);
bool write(float value);
+ bool write(const std::vector<AttributionNode>& nodes);
+ bool write(const AttributionNode& node);
/**
* Return a string representation of this event.
@@ -98,11 +117,6 @@
*/
void ToProto(android::util::ProtoOutputStream& out) const;
- /*
- * Get a KeyValuePair proto object.
- */
- KeyValuePair GetKeyValueProto(size_t key) const;
-
/**
* Used with the constructor where tag is passed in. Converts the log_event_list to read mode
* and prepares the list for reading.
@@ -114,10 +128,12 @@
*/
void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;}
- int size() const {
- return mElements.size();
+ inline int size() const {
+ return mFieldValueMap.size();
}
+ inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; }
+
private:
/**
* Don't copy, it's slower. If we really need this we can add it but let's try to
@@ -130,8 +146,11 @@
*/
void init(android_log_context context);
- vector<android_log_list_element> mElements;
+ FieldValueMap mFieldValueMap;
+ // This field is used when statsD wants to create log event object and write fields to it. After
+ // calling init() function, this object would be destroyed to save memory usage.
+ // When the log event is created from log msg, this field is never initiated.
android_log_context mContext;
uint64_t mTimestampNs;
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
index 51a38b6..bd5e3cb 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -82,8 +82,8 @@
mChildren.push_back(childIndex);
- const set<int>& childTagIds = allTrackers[childIndex]->getTagIds();
- mTagIds.insert(childTagIds.begin(), childTagIds.end());
+ const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds();
+ mAtomIds.insert(childTagIds.begin(), childTagIds.end());
}
mInitialized = true;
@@ -100,7 +100,7 @@
return;
}
- if (mTagIds.find(event.GetTagId()) == mTagIds.end()) {
+ if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
matcherResults[mIndex] = MatchingState::kNotMatched;
return;
}
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 8162c44..567944f 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -65,8 +65,8 @@
// Get the tagIds that this matcher cares about. The combined collection is stored
// in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
// some memory but hopefully it can save us much CPU time when there is flood of events.
- virtual const std::set<int>& getTagIds() const {
- return mTagIds;
+ virtual const std::set<int>& getAtomIds() const {
+ return mAtomIds;
}
const std::string& getName() const {
@@ -88,7 +88,7 @@
// useful when we have a complex CombinationLogMatcherTracker.
// TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
// LogMatchingTracker cares is only a few.
- std::set<int> mTagIds;
+ std::set<int> mAtomIds;
};
} // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index ac217ab..91ef034 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -30,12 +30,13 @@
SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
- const SimpleAtomMatcher& matcher)
- : LogMatchingTracker(name, index), mMatcher(matcher) {
- if (!matcher.has_tag()) {
+ const SimpleAtomMatcher& matcher,
+ const UidMap& uidMap)
+ : LogMatchingTracker(name, index), mMatcher(matcher), mUidMap(uidMap) {
+ if (!matcher.has_atom_id()) {
mInitialized = false;
} else {
- mTagIds.insert(matcher.tag());
+ mAtomIds.insert(matcher.atom_id());
mInitialized = true;
}
}
@@ -59,12 +60,12 @@
return;
}
- if (mTagIds.find(event.GetTagId()) == mTagIds.end()) {
+ if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
matcherResults[mIndex] = MatchingState::kNotMatched;
return;
}
- bool matched = matchesSimple(mMatcher, event);
+ bool matched = matchesSimple(mUidMap, mMatcher, event);
matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
}
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index 2c188c1..68518f8 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -24,6 +24,7 @@
#include <vector>
#include "LogMatchingTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
namespace android {
namespace os {
@@ -32,7 +33,8 @@
class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
public:
SimpleLogMatchingTracker(const std::string& name, const int index,
- const SimpleAtomMatcher& matcher);
+ const SimpleAtomMatcher& matcher,
+ const UidMap& uidMap);
~SimpleLogMatchingTracker();
@@ -47,6 +49,7 @@
private:
const SimpleAtomMatcher mMatcher;
+ const UidMap& mUidMap;
};
} // namespace statsd
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 9e88e5d0..46d9b92 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -19,7 +19,9 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
+#include "dimension.h"
#include "stats_util.h"
+#include "field_util.h"
#include <log/event_tag_map.h>
#include <log/log_event_list.h>
@@ -91,127 +93,173 @@
return matched;
}
-bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) {
- const int tagId = event.GetTagId();
+bool IsAttributionUidField(const Field& field) {
+ return field.child_size() == 1 && field.child(0).field() == 1
+ && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1;
+}
- if (simpleMatcher.tag() != tagId) {
- return false;
- }
- // now see if this event is interesting to us -- matches ALL the matchers
- // defined in the metrics.
- bool allMatched = true;
- for (int j = 0; allMatched && j < simpleMatcher.key_value_matcher_size(); j++) {
- auto cur = simpleMatcher.key_value_matcher(j);
-
- // TODO: Check if this key is a magic key (eg package name).
- // TODO: Maybe make packages a different type in the config?
- int key = cur.key_matcher().key();
-
- const KeyValueMatcher::ValueMatcherCase matcherCase = cur.value_matcher_case();
- if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqString) {
- // String fields
- status_t err = NO_ERROR;
- const char* val = event.GetString(key, &err);
- if (err == NO_ERROR && val != NULL) {
- if (!(cur.eq_string() == val)) {
- allMatched = false;
- break;
- }
- } else {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
- matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt ||
- matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt ||
- matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt ||
- matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
- // Integer fields
- status_t err = NO_ERROR;
- int64_t val = event.GetLong(key, &err);
- if (err == NO_ERROR) {
- if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt) {
- if (!(val == cur.eq_int())) {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt) {
- if (!(val < cur.lt_int())) {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt) {
- if (!(val > cur.gt_int())) {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt) {
- if (!(val <= cur.lte_int())) {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
- if (!(val >= cur.gte_int())) {
- allMatched = false;
- break;
- }
- }
- } else {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
- // Boolean fields
- status_t err = NO_ERROR;
- bool val = event.GetBool(key, &err);
- if (err == NO_ERROR) {
- if (!(cur.eq_bool() == val)) {
- allMatched = false;
- break;
- }
- } else {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
- matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
- // Float fields
- status_t err = NO_ERROR;
- float val = event.GetFloat(key, &err);
- if (err == NO_ERROR) {
- if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) {
- if (!(val < cur.lt_float())) {
- allMatched = false;
- break;
- }
- } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
- if (!(val > cur.gt_float())) {
- allMatched = false;
- break;
- }
- }
- } else {
- allMatched = false;
- break;
- }
- } else {
- // If value matcher is not present, assume that we match.
+bool matchesNonRepeatedField(
+ const UidMap& uidMap,
+ const FieldValueMap& fieldMap,
+ const FieldValueMatcher&matcher,
+ const Field& field) {
+ if (matcher.value_matcher_case() ==
+ FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) {
+ return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field();
+ } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) {
+ bool allMatched = true;
+ for (int i = 0; allMatched && i < matcher.matches_tuple().field_value_matcher_size(); ++i) {
+ const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i);
+ Field childField = field;
+ appendLeaf(&childField, childMatcher.field());
+ allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, childField);
}
+ return allMatched;
+ } else {
+ auto ret = fieldMap.equal_range(field);
+ int found = 0;
+ for (auto it = ret.first; it != ret.second; ++it) {
+ found++;
+ }
+ // Not found.
+ if (found <= 0) {
+ return false;
+ }
+ if (found > 1) {
+ ALOGE("Found multiple values for optional field.");
+ return false;
+ }
+ bool matched = false;
+ switch (matcher.value_matcher_case()) {
+ case FieldValueMatcher::ValueMatcherCase::kEqBool:
+ // Logd does not support bool, it is int instead.
+ matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kEqString:
+ {
+ if (IsAttributionUidField(field)) {
+ const int uid = ret.first->second.value_int();
+ std::set<string> packageNames =
+ uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+ matched = packageNames.find(matcher.eq_string()) != packageNames.end();
+ } else {
+ matched = (ret.first->second.value_str() == matcher.eq_string());
+ }
+ }
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kEqInt:
+ matched = (ret.first->second.value_int() == matcher.eq_int());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kLtInt:
+ matched = (ret.first->second.value_int() < matcher.lt_int());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kGtInt:
+ matched = (ret.first->second.value_int() > matcher.gt_int());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kLtFloat:
+ matched = (ret.first->second.value_float() < matcher.lt_float());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kGtFloat:
+ matched = (ret.first->second.value_float() > matcher.gt_float());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kLteInt:
+ matched = (ret.first->second.value_int() <= matcher.lte_int());
+ break;
+ case FieldValueMatcher::ValueMatcherCase::kGteInt:
+ matched = (ret.first->second.value_int() >= matcher.gte_int());
+ break;
+ default:
+ break;
+ }
+ return matched;
}
- return allMatched;
}
-vector<KeyValuePair> getDimensionKey(const LogEvent& event,
- const std::vector<KeyMatcher>& dimensions) {
- vector<KeyValuePair> key;
- key.reserve(dimensions.size());
- for (const KeyMatcher& dimension : dimensions) {
- KeyValuePair k = event.GetKeyValueProto(dimension.key());
- key.push_back(k);
+bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
+ const FieldValueMatcher&matcher, const Field& field) {
+ if (matcher.position() == Position::FIRST) {
+ Field first_field = field;
+ setPositionForLeaf(&first_field, 0);
+ return matchesNonRepeatedField(uidMap, fieldMap, matcher, first_field);
+ } else {
+ auto itLower = fieldMap.lower_bound(field);
+ if (itLower == fieldMap.end()) {
+ return false;
+ }
+ Field next_field = field;
+ getNextField(&next_field);
+ auto itUpper = fieldMap.lower_bound(next_field);
+ switch (matcher.position()) {
+ case Position::LAST:
+ {
+ itUpper--;
+ if (itUpper == fieldMap.end()) {
+ return false;
+ } else {
+ Field last_field = field;
+ int last_index = getPositionByReferenceField(field, itUpper->first);
+ if (last_index < 0) {
+ return false;
+ }
+ setPositionForLeaf(&last_field, last_index);
+ return matchesNonRepeatedField(uidMap, fieldMap, matcher, last_field);
+ }
+ }
+ break;
+ case Position::ANY:
+ {
+ std::set<int> indexes;
+ for (auto it = itLower; it != itUpper; ++it) {
+ int index = getPositionByReferenceField(field, it->first);
+ if (index >= 0) {
+ indexes.insert(index);
+ }
+ }
+ bool matched = false;
+ for (const int index : indexes) {
+ Field any_field = field;
+ setPositionForLeaf(&any_field, index);
+ matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, any_field);
+ }
+ return matched;
+ }
+ default:
+ return false;
+ }
}
- return key;
+
}
+bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
+ const FieldValueMatcher&matcher, const Field& field) {
+ if (!matcher.has_position()) {
+ return matchesNonRepeatedField(uidMap, fieldMap, matcher, field);
+ } else {
+ return matchesRepeatedField(uidMap, fieldMap, matcher, field);
+ }
+}
+
+bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
+ const LogEvent& event) {
+ if (simpleMatcher.field_value_matcher_size() <= 0) {
+ return event.GetTagId() == simpleMatcher.atom_id();
+ }
+ Field root_field;
+ root_field.set_field(simpleMatcher.atom_id());
+ FieldValueMatcher root_field_matcher;
+ root_field_matcher.set_field(simpleMatcher.atom_id());
+ for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) {
+ *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() =
+ simpleMatcher.field_value_matcher(i);
+ }
+ return matchFieldSimple(uidMap, event.getFieldValueMap(), root_field_matcher, root_field);
+}
+
+vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher) {
+ vector<DimensionsValue> values;
+ findDimensionsValues(event.getFieldValueMap(), matcher, &values);
+ return values;
+}
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index f54ab36..704cb4c 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef MATCHER_UTIL_H
-#define MATCHER_UTIL_H
+#pragma once
#include "logd/LogEvent.h"
@@ -28,6 +27,7 @@
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
+#include "packages/UidMap.h"
namespace android {
namespace os {
@@ -42,12 +42,14 @@
bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
const std::vector<MatchingState>& matcherResults);
-bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& dimensionsMap,
+ const FieldValueMatcher& matcher, const Field& field);
-std::vector<KeyValuePair> getDimensionKey(const LogEvent& event,
- const std::vector<KeyMatcher>& dimensions);
+bool matchesSimple(const UidMap& uidMap,
+ const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+
+std::vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher);
} // namespace statsd
} // namespace os
} // namespace android
-#endif // MATCHER_UTIL_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 9031ed0..5bf3cff 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -20,6 +20,7 @@
#include "CountMetricProducer.h"
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
+#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
@@ -51,12 +52,6 @@
// for CountMetricData
const int FIELD_ID_DIMENSION = 1;
const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
// for CountBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -75,7 +70,7 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+ mDimensions = metric.dimensions();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -95,6 +90,24 @@
VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
+void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+ flushIfNeededLocked(dumpTimeNs);
+ report->set_metric_name(mName);
+ report->set_start_report_nanos(mStartTimeNs);
+
+ auto count_metrics = report->mutable_count_metrics();
+ for (const auto& counter : mPastBuckets) {
+ CountMetricData* metricData = count_metrics->add_data();
+ *metricData->mutable_dimension() = counter.first.getDimensionsValue();
+ for (const auto& bucket : counter.second) {
+ CountBucketInfo* bucketInfo = metricData->add_bucket_info();
+ bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+ bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+ bucketInfo->set_count(bucket.mCount);
+ }
+ }
+}
+
void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
ProtoOutputStream* protoOutput) {
flushIfNeededLocked(dumpTimeNs);
@@ -107,28 +120,16 @@
for (const auto& counter : mPastBuckets) {
const HashableDimensionKey& hashableKey = counter.first;
- const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
VLOG(" dimension key %s", hashableKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- // First fill dimension (KeyValuePairs).
- for (const auto& kv : kvs) {
- long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
- if (kv.has_value_str()) {
- protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
- } else if (kv.has_value_int()) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
- } else if (kv.has_value_bool()) {
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
- } else if (kv.has_value_float()) {
- protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
- }
- protoOutput->end(dimensionToken);
- }
+ // First fill dimension.
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionToken);
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -183,7 +184,7 @@
void CountMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index e32fc06..6087ae5 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -51,12 +51,13 @@
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
private:
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 51ea4b5..30b105c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -20,6 +20,7 @@
#include "DurationMetricProducer.h"
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
+#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
@@ -50,12 +51,6 @@
// for DurationMetricData
const int FIELD_ID_DIMENSION = 1;
const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
// for DurationBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -66,7 +61,7 @@
const size_t stopIndex, const size_t stopAllIndex,
const bool nesting,
const sp<ConditionWizard>& wizard,
- const vector<KeyMatcher>& internalDimension,
+ const FieldMatcher& internalDimensions,
const uint64_t startTimeNs)
: MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
mAggregationType(metric.aggregation_type()),
@@ -74,7 +69,7 @@
mStopIndex(stopIndex),
mStopAllIndex(stopAllIndex),
mNested(nesting),
- mInternalDimension(internalDimension) {
+ mInternalDimensions(internalDimensions) {
// 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
// not related. Maybe we should add a template in the future??
@@ -85,7 +80,7 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+ mDimensions = metric.dimensions();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -151,6 +146,24 @@
}
}
+void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+ flushIfNeededLocked(dumpTimeNs);
+ report->set_metric_name(mName);
+ report->set_start_report_nanos(mStartTimeNs);
+
+ auto duration_metrics = report->mutable_duration_metrics();
+ for (const auto& pair : mPastBuckets) {
+ DurationMetricData* metricData = duration_metrics->add_data();
+ *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+ for (const auto& bucket : pair.second) {
+ auto bucketInfo = metricData->add_bucket_info();
+ bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+ bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+ bucketInfo->set_duration_nanos(bucket.mDuration);
+ }
+ }
+}
+
void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
ProtoOutputStream* protoOutput) {
flushIfNeededLocked(dumpTimeNs);
@@ -163,28 +176,16 @@
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
- const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
VLOG(" dimension key %s", hashableKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- // First fill dimension (KeyValuePairs).
- for (const auto& kv : kvs) {
- long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
- if (kv.has_value_str()) {
- protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
- } else if (kv.has_value_int()) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
- } else if (kv.has_value_bool()) {
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
- } else if (kv.has_value_float()) {
- protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
- }
- protoOutput->end(dimensionToken);
- }
+ // First fill dimension.
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionToken);
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
@@ -249,7 +250,7 @@
void DurationMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+ const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) {
flushIfNeededLocked(event.GetTimestampNs());
@@ -260,7 +261,6 @@
return;
}
- HashableDimensionKey atomKey(getDimensionKey(event, mInternalDimension));
if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
if (hitGuardRailLocked(eventKey)) {
@@ -271,11 +271,25 @@
auto it = mCurrentSlicedDuration.find(eventKey);
- if (matcherIndex == mStartIndex) {
- it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
- } else if (matcherIndex == mStopIndex) {
- it->second->noteStop(atomKey, event.GetTimestampNs(), false);
+ std::vector<DimensionsValue> values = getDimensionKeys(event, mInternalDimensions);
+ if (values.empty()) {
+ if (matcherIndex == mStartIndex) {
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
+ event.GetTimestampNs(), conditionKeys);
+ } else if (matcherIndex == mStopIndex) {
+ it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false);
+ }
+ } else {
+ for (const DimensionsValue& value : values) {
+ if (matcherIndex == mStartIndex) {
+ it->second->noteStart(HashableDimensionKey(value), condition,
+ event.GetTimestampNs(), conditionKeys);
+ } else if (matcherIndex == mStopIndex) {
+ it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+ }
+ }
}
+
}
size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index abc89bd..e06b9a1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,7 @@
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);
+ const FieldMatcher& internalDimensions, const uint64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -51,12 +51,13 @@
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKeys, bool condition,
+ const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) override;
private:
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -85,7 +86,7 @@
const bool mNested;
// The dimension from the atom predicate. e.g., uid, wakelock name.
- const vector<KeyMatcher> mInternalDimension;
+ const FieldMatcher mInternalDimensions;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
@@ -109,6 +110,7 @@
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+ FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 6a072b0..c8138d3 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -96,6 +96,10 @@
return buffer;
}
+void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+
+}
+
void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
ProtoOutputStream* protoOutput) {
protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
@@ -121,7 +125,7 @@
void EventMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (!condition) {
return;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 6120ad8..a57b07d 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -46,11 +46,12 @@
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 47cca0e..51fd9fc 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -19,6 +19,8 @@
#include "GaugeMetricProducer.h"
#include "guardrail/StatsdStats.h"
+#include "dimension.h"
+#include "stats_log_util.h"
#include <cutils/log.h>
@@ -51,12 +53,6 @@
// for GaugeMetricData
const int FIELD_ID_DIMENSION = 1;
const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
// for GaugeBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -71,18 +67,18 @@
mStatsPullerManager(statsPullerManager),
mPullTagId(pullTagId),
mAtomTagId(atomTagId) {
+ mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
+ mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
} else {
mBucketSizeNs = kDefaultGaugemBucketSizeNs;
}
- for (int i = 0; i < metric.gauge_fields().field_num_size(); i++) {
- mGaugeFields.push_back(metric.gauge_fields().field_num(i));
- }
+ mFieldFilter = metric.gauge_fields_filter();
// TODO: use UidMap if uid->pkg_name is required
- mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+ mDimensions = metric.dimensions();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -93,7 +89,7 @@
// Kicks off the puller immediately.
if (mPullTagId != -1) {
mStatsPullerManager->RegisterReceiver(mPullTagId, this,
- metric.bucket().bucket_size_millis());
+ metric.bucket().bucket_size_millis());
}
VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
@@ -116,6 +112,10 @@
}
}
+void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+ flushIfNeededLocked(dumpTimeNs);
+}
+
void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
ProtoOutputStream* protoOutput) {
VLOG("gauge metric %s dump report now...", mName.c_str());
@@ -128,28 +128,16 @@
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
- const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
VLOG(" dimension key %s", hashableKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- // First fill dimension (KeyValuePairs).
- for (const auto& kv : kvs) {
- long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
- if (kv.has_value_str()) {
- protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
- } else if (kv.has_value_int()) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
- } else if (kv.has_value_bool()) {
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
- } else if (kv.has_value_float()) {
- protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
- }
- protoOutput->end(dimensionToken);
- }
+ // First fill dimension.
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionToken);
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
@@ -160,25 +148,11 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
(long long)bucket.mBucketEndNs);
long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM);
- long long eventToken = protoOutput->start(FIELD_TYPE_MESSAGE | mAtomTagId);
- for (const auto& pair : bucket.mEvent->kv) {
- if (pair.has_value_int()) {
- protoOutput->write(FIELD_TYPE_INT32 | pair.key(), pair.value_int());
- } else if (pair.has_value_long()) {
- protoOutput->write(FIELD_TYPE_INT64 | pair.key(), pair.value_long());
- } else if (pair.has_value_str()) {
- protoOutput->write(FIELD_TYPE_STRING | pair.key(), pair.value_str());
- } else if (pair.has_value_long()) {
- protoOutput->write(FIELD_TYPE_FLOAT | pair.key(), pair.value_float());
- } else if (pair.has_value_bool()) {
- protoOutput->write(FIELD_TYPE_BOOL | pair.key(), pair.value_bool());
- }
- }
- protoOutput->end(eventToken);
+ writeFieldValueTreeToStream(*bucket.mGaugeFields, protoOutput);
protoOutput->end(atomToken);
protoOutput->end(bucketInfoToken);
- VLOG("\t bucket [%lld - %lld] content: %s", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, bucket.mEvent->ToString().c_str());
+ VLOG("\t bucket [%lld - %lld] includes %d gauge fields.", (long long)bucket.mBucketStartNs,
+ (long long)bucket.mBucketEndNs, (int)bucket.mGaugeFields->size());
}
protoOutput->end(wrapperToken);
}
@@ -223,18 +197,13 @@
VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
-shared_ptr<EventKV> GaugeMetricProducer::getGauge(const LogEvent& event) {
- shared_ptr<EventKV> ret = make_shared<EventKV>();
- if (mGaugeFields.size() == 0) {
- for (int i = 1; i <= event.size(); i++) {
- ret->kv.push_back(event.GetKeyValueProto(i));
- }
- } else {
- for (int i = 0; i < (int)mGaugeFields.size(); i++) {
- ret->kv.push_back(event.GetKeyValueProto(mGaugeFields[i]));
- }
+std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
+ std::shared_ptr<FieldValueMap> gaugeFields =
+ std::make_shared<FieldValueMap>(event.getFieldValueMap());
+ if (!mFieldFilter.include_all()) {
+ filterFields(mFieldFilter.fields(), gaugeFields.get());
}
- return ret;
+ return gaugeFields;
}
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
@@ -268,7 +237,7 @@
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (condition == false) {
return;
@@ -285,21 +254,21 @@
if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end()) {
return;
}
- shared_ptr<EventKV> gauge = getGauge(event);
+ std::shared_ptr<FieldValueMap> gaugeFields = getGaugeFields(event);
if (hitGuardRailLocked(eventKey)) {
return;
}
- (*mCurrentSlicedBucket)[eventKey] = gauge;
+ (*mCurrentSlicedBucket)[eventKey] = gaugeFields;
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
if (mAnomalyTrackers.size() > 0) {
- if (gauge->kv.size() == 1) {
- KeyValuePair pair = gauge->kv[0];
+ if (gaugeFields->size() == 1) {
+ const DimensionsValue& dimensionsValue = gaugeFields->begin()->second;
long gaugeVal = 0;
- if (pair.has_value_int()) {
- gaugeVal = (long)pair.value_int();
- } else if (pair.has_value_long()) {
- gaugeVal = pair.value_long();
+ if (dimensionsValue.has_value_int()) {
+ gaugeVal = (long)dimensionsValue.value_int();
+ } else if (dimensionsValue.has_value_long()) {
+ gaugeVal = dimensionsValue.value_long();
}
for (auto& tracker : mAnomalyTrackers) {
tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
@@ -313,12 +282,12 @@
mCurrentSlicedBucketForAnomaly->clear();
status_t err = NO_ERROR;
for (const auto& slice : *mCurrentSlicedBucket) {
- KeyValuePair pair = slice.second->kv[0];
+ const DimensionsValue& dimensionsValue = slice.second->begin()->second;
long gaugeVal = 0;
- if (pair.has_value_int()) {
- gaugeVal = (long)pair.value_int();
- } else if (pair.has_value_long()) {
- gaugeVal = pair.value_long();
+ if (dimensionsValue.has_value_int()) {
+ gaugeVal = (long)dimensionsValue.value_int();
+ } else if (dimensionsValue.has_value_long()) {
+ gaugeVal = dimensionsValue.value_long();
}
(*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
}
@@ -342,11 +311,10 @@
info.mBucketNum = mCurrentBucketNum;
for (const auto& slice : *mCurrentSlicedBucket) {
- info.mEvent = slice.second;
+ info.mGaugeFields = slice.second;
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
- VLOG("gauge metric %s, dump key value: %s -> %s", mName.c_str(),
- slice.first.c_str(), slice.second->ToString().c_str());
+ VLOG("gauge metric %s, dump key value: %s", mName.c_str(), slice.first.c_str());
}
// Reset counters
@@ -357,7 +325,7 @@
}
}
- mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
+ mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
// Adjusts the bucket start time
int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 19d51e8..2a6401d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,13 @@
struct GaugeBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
- std::shared_ptr<EventKV> mEvent;
+ std::shared_ptr<FieldValueMap> mGaugeFields;
uint64_t mBucketNum;
};
+typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<FieldValueMap>>
+ DimToGaugeFieldsMap;
+
// This gauge metric producer first register the puller to automatically pull the gauge at the
// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
@@ -57,12 +60,13 @@
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
private:
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
// for testing
GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
@@ -94,10 +98,10 @@
std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
- std::shared_ptr<DimToEventKVMap> mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
+ std::shared_ptr<DimToGaugeFieldsMap> mCurrentSlicedBucket;
// The current bucket for anomaly detection.
- std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
+ std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
// Translate Atom based bucket to single numeric value bucket for anomaly
void updateCurrentSlicedBucketForAnomaly();
@@ -105,10 +109,10 @@
int mAtomTagId;
// Whitelist of fields to report. Empty means all are reported.
- std::vector<int> mGaugeFields;
+ FieldFilter mFieldFilter;
// apply a whitelist on the original input
- std::shared_ptr<EventKV> getGauge(const LogEvent& event);
+ std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 5286908..4ed8289 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -28,22 +28,12 @@
return;
}
- HashableDimensionKey eventKey;
-
- if (mDimension.size() > 0) {
- vector<KeyValuePair> key = getDimensionKey(event, mDimension);
- eventKey = HashableDimensionKey(key);
- } else {
- eventKey = DEFAULT_DIMENSION_KEY;
- }
-
bool condition;
-
- map<string, HashableDimensionKey> conditionKeys;
+ map<string, std::vector<HashableDimensionKey>> conditionKeys;
if (mConditionSliced) {
for (const auto& link : mConditionLinks) {
- HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
- conditionKeys[link.condition()] = conditionKey;
+ conditionKeys.insert(std::make_pair(link.condition(),
+ getDimensionKeysForCondition(event, link)));
}
if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
condition = false;
@@ -53,7 +43,17 @@
} else {
condition = mCondition;
}
- onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event);
+
+ if (mDimensions.child_size() > 0) {
+ vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions);
+ for (const DimensionsValue& dimensionValue : dimensionValues) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, HashableDimensionKey(dimensionValue), conditionKeys, condition, event);
+ }
+ } else {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, DEFAULT_DIMENSION_KEY, conditionKeys, condition, event);
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 647d8c1..fe1a53b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -50,6 +50,7 @@
mConditionSliced(false),
mWizard(wizard),
mConditionTrackerIndex(conditionIndex){};
+
virtual ~MetricProducer(){};
void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{
@@ -90,6 +91,10 @@
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, protoOutput);
}
+ void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return onDumpReportLocked(dumpTimeNs, report);
+ }
// Returns the memory in bytes currently used to store this metric's data. Does not change
// state.
@@ -112,11 +117,16 @@
return mBucketSizeNs;
}
+ inline const string& getName() {
+ return mName;
+ }
+
protected:
virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0;
virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0;
virtual void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) = 0;
+ virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0;
virtual size_t byteSizeLocked() const = 0;
const std::string mName;
@@ -140,7 +150,7 @@
int mConditionTrackerIndex;
- std::vector<KeyMatcher> mDimension; // The dimension defined in statsd_config
+ FieldMatcher mDimensions; // The dimension defined in statsd_config
std::vector<MetricConditionLink> mConditionLinks;
@@ -163,7 +173,7 @@
*/
virtual void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) = 0;
// Consume the parsed stats log entry that already matched the "what" of the metric.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 231bd8e..9749e0f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -49,7 +49,7 @@
const long timeBaseSec, sp<UidMap> uidMap)
: mConfigKey(key), mUidMap(uidMap) {
mConfigValid =
- initStatsdConfig(key, config, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
+ initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
mTrackerToMetricMap, mTrackerToConditionMap);
@@ -142,6 +142,12 @@
initLogSourceWhiteList();
}
+void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) {
+ for (auto& producer : mAllMetricProducers) {
+ producer->onDumpReport(dumpTimeStampNs, report->add_metrics());
+ }
+}
+
void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) {
VLOG("=========================Metric Reports Start==========================");
uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 8faa75d..51c4c90 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -45,8 +45,9 @@
void onLogEvent(const LogEvent& event);
- void onAnomalyAlarmFired(const uint64_t timestampNs,
- unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
+ void onAnomalyAlarmFired(
+ const uint64_t timestampNs,
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
@@ -58,6 +59,7 @@
// Config source owner can call onDumpReport() to get all the metrics collected.
virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
+ virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
// Computes the total byte size of all metrics managed by a single config source.
// Does not change the state.
@@ -128,6 +130,9 @@
std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
void initLogSourceWhiteList();
+
+ FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+ FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 40aed7b..b58dc68 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -19,6 +19,7 @@
#include "ValueMetricProducer.h"
#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
#include <cutils/log.h>
#include <limits.h>
@@ -54,12 +55,6 @@
// for ValueMetricData
const int FIELD_ID_DIMENSION = 1;
const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
// for ValueBucketInfo
const int FIELD_ID_START_BUCKET_NANOS = 1;
const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -84,7 +79,7 @@
mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
}
- mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+ mDimensions = metric.dimensions();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -121,6 +116,23 @@
VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
}
+void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+ flushIfNeededLocked(dumpTimeNs);
+ report->set_metric_name(mName);
+ report->set_start_report_nanos(mStartTimeNs);
+ auto value_metrics = report->mutable_value_metrics();
+ for (const auto& pair : mPastBuckets) {
+ ValueMetricData* metricData = value_metrics->add_data();
+ *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+ for (const auto& bucket : pair.second) {
+ ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
+ bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+ bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+ bucketInfo->set_value(bucket.mValue);
+ }
+ }
+}
+
void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
ProtoOutputStream* protoOutput) {
VLOG("metric %s dump report now...", mName.c_str());
@@ -132,26 +144,14 @@
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
VLOG(" dimension key %s", hashableKey.c_str());
- const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- // First fill dimension (KeyValuePairs).
- for (const auto& kv : kvs) {
- long long dimensionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
- protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
- if (kv.has_value_str()) {
- protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
- } else if (kv.has_value_int()) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
- } else if (kv.has_value_bool()) {
- protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
- } else if (kv.has_value_float()) {
- protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
- }
- protoOutput->end(dimensionToken);
- }
+ // First fill dimension.
+ long long dimensionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+ writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionToken);
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
@@ -256,7 +256,7 @@
void ValueMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const map<string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 2f27e4e..6f3b1d1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -50,12 +50,13 @@
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const HashableDimensionKey& eventKey,
- const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+ const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
private:
void onDumpReportLocked(const uint64_t dumpTimeNs,
android::util::ProtoOutputStream* protoOutput) override;
+ void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 658b732..97dbf7a 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -49,7 +49,7 @@
ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str());
return false;
}
- if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
+ if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, "
"the \"what\" can only about one atom type.",
what.c_str());
@@ -92,7 +92,8 @@
return true;
}
-bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
+bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
+ unordered_map<string, int>& logTrackerMap,
vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
vector<AtomMatcher> matcherConfigs;
const int atomMatcherCount = config.atom_matcher_size();
@@ -106,7 +107,7 @@
switch (logMatcher.contents_case()) {
case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
allAtomMatchers.push_back(new SimpleLogMatchingTracker(
- logMatcher.name(), index, logMatcher.simple_atom_matcher()));
+ logMatcher.name(), index, logMatcher.simple_atom_matcher(), uidMap));
break;
case AtomMatcher::ContentsCase::kCombination:
allAtomMatchers.push_back(
@@ -131,7 +132,7 @@
return false;
}
// Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
- const set<int>& tagIds = matcher->getTagIds();
+ const set<int>& tagIds = matcher->getAtomIds();
allTagIds.insert(tagIds.begin(), tagIds.end());
}
return true;
@@ -205,10 +206,13 @@
// Align all buckets to same instant in MIN_BUCKET_SIZE_SEC, so that avoid alarm
// clock will not grow very aggressive. New metrics will be delayed up to
// MIN_BUCKET_SIZE_SEC before starting.
- long currentTimeSec = time(nullptr);
- uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec -
- (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) *
- NS_PER_SEC;
+ // Why not use timeBaseSec directly?
+// long currentTimeSec = time(nullptr);
+// uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec -
+// (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) *
+// NS_PER_SEC;
+
+ uint64_t startTimeNs = timeBaseSec * NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
@@ -222,7 +226,7 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
@@ -274,7 +278,7 @@
int trackerIndices[3] = {-1, -1, -1};
if (!simplePredicate.has_start() ||
!handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
- metric.dimension_size() > 0, allAtomMatchers,
+ metric.has_dimensions(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
ALOGE("Duration metrics must specify a valid the start event matcher");
return false;
@@ -282,21 +286,19 @@
if (simplePredicate.has_stop() &&
!handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
- metric.dimension_size() > 0, allAtomMatchers,
+ metric.has_dimensions(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
return false;
}
if (simplePredicate.has_stop_all() &&
!handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
- metric.dimension_size() > 0, allAtomMatchers,
+ metric.has_dimensions(), allAtomMatchers,
logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
return false;
}
- vector<KeyMatcher> internalDimension;
- internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(),
- simplePredicate.dimension().end());
+ FieldMatcher internalDimensions = simplePredicate.dimensions();
int conditionIndex = -1;
@@ -316,7 +318,7 @@
sp<MetricProducer> durationMetric = new DurationMetricProducer(
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimension, startTimeNs);
+ trackerIndices[2], nesting, wizard, internalDimensions, startTimeNs);
allMetricProducers.push_back(durationMetric);
}
@@ -368,7 +370,7 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
@@ -376,10 +378,10 @@
sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
- if (atomMatcher->getTagIds().size() != 1) {
+ if (atomMatcher->getAtomIds().size() != 1) {
return false;
}
- int atomTagId = *(atomMatcher->getTagIds().begin());
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
@@ -410,15 +412,15 @@
return false;
}
- if ((!metric.gauge_fields().has_include_all() ||
- (metric.gauge_fields().include_all() == false)) &&
- metric.gauge_fields().field_num_size() == 0) {
+ if ((!metric.gauge_fields_filter().has_include_all() ||
+ (metric.gauge_fields_filter().include_all() == false)) &&
+ !hasLeafNode(metric.gauge_fields_filter().fields())) {
ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
return false;
}
- if ((metric.gauge_fields().has_include_all() &&
- metric.gauge_fields().include_all() == true) &&
- metric.gauge_fields().field_num_size() > 0) {
+ if ((metric.gauge_fields_filter().has_include_all() &&
+ metric.gauge_fields_filter().include_all() == true) &&
+ hasLeafNode(metric.gauge_fields_filter().fields())) {
ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
return false;
}
@@ -426,7 +428,7 @@
int metricIndex = allMetricProducers.size();
metricMap.insert({metric.name(), metricIndex});
int trackerIndex;
- if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+ if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
allAtomMatchers, logTrackerMap, trackerToMetricMap,
trackerIndex)) {
return false;
@@ -434,10 +436,10 @@
sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
// If it is pulled atom, it should be simple matcher with one tagId.
- if (atomMatcher->getTagIds().size() != 1) {
+ if (atomMatcher->getAtomIds().size() != 1) {
return false;
}
- int atomTagId = *(atomMatcher->getTagIds().begin());
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
@@ -462,7 +464,8 @@
return true;
}
-bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap,
+bool initAlerts(const StatsdConfig& config,
+ const unordered_map<string, int>& metricProducerMap,
vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
for (int i = 0; i < config.alert_size(); i++) {
@@ -488,7 +491,9 @@
return true;
}
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
+ const UidMap& uidMap,
+ const long timeBaseSec, set<int>& allTagIds,
vector<sp<LogMatchingTracker>>& allAtomMatchers,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
@@ -500,7 +505,7 @@
unordered_map<string, int> conditionTrackerMap;
unordered_map<string, int> metricProducerMap;
- if (!initLogTrackers(config, logTrackerMap, allAtomMatchers, allTagIds)) {
+ if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3337332..9ad5176 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -43,6 +43,7 @@
// [allAtomMatchers]: should store the sp to all the LogMatchingTracker
// [allTagIds]: contains the set of all interesting tag ids to this config.
bool initLogTrackers(const StatsdConfig& config,
+ const UidMap& uidMap,
std::unordered_map<std::string, int>& logTrackerMap,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::set<int>& allTagIds);
@@ -89,7 +90,10 @@
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, std::set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
+
+ const UidMap& uidMap,
+ const long timeBaseSec, std::set<int>& allTagIds,
std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
std::vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 21a9cf3..416b87b 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -404,4 +404,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index a9aec94..02dea54 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -168,4 +168,3 @@
} // namespace statsd
} // namespace os
} // namespace android
-
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 3c85c57..f5282ea 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -24,8 +24,14 @@
import "frameworks/base/cmds/statsd/src/atoms.proto";
-message KeyValuePair {
- optional int32 key = 1;
+message Field {
+ optional int32 field = 1;
+ optional int32 position_index = 2 [default = -1];
+ repeated Field child = 3;
+}
+
+message DimensionsValue {
+ optional int32 field = 1;
oneof value {
string value_str = 2;
@@ -33,9 +39,14 @@
int64 value_long = 4;
bool value_bool = 5;
float value_float = 6;
+ DimensionsValueTuple value_tuple = 7;
}
}
+message DimensionsValueTuple {
+ repeated DimensionsValue dimensions_value = 1;
+}
+
message EventMetricData {
optional int64 timestamp_nanos = 1;
@@ -51,7 +62,7 @@
}
message CountMetricData {
- repeated KeyValuePair dimension = 1;
+ optional DimensionsValue dimension = 1;
repeated CountBucketInfo bucket_info = 2;
}
@@ -65,7 +76,7 @@
}
message DurationMetricData {
- repeated KeyValuePair dimension = 1;
+ optional DimensionsValue dimension = 1;
repeated DurationBucketInfo bucket_info = 2;
}
@@ -79,7 +90,7 @@
}
message ValueMetricData {
- repeated KeyValuePair dimension = 1;
+ optional DimensionsValue dimension = 1;
repeated ValueBucketInfo bucket_info = 2;
}
@@ -93,7 +104,7 @@
}
message GaugeMetricData {
- repeated KeyValuePair dimension = 1;
+ optional DimensionsValue dimension = 1;
repeated GaugeBucketInfo bucket_info = 2;
}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
new file mode 100644
index 0000000..476e117
--- /dev/null
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stats_log_util.h"
+
+#include <set>
+#include <stack>
+#include <utils/Log.h>
+
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// for DimensionsValue Proto
+const int DIMENSIONS_VALUE_FIELD = 1;
+const int DIMENSIONS_VALUE_VALUE_STR = 2;
+const int DIMENSIONS_VALUE_VALUE_INT = 3;
+const int DIMENSIONS_VALUE_VALUE_LONG = 4;
+const int DIMENSIONS_VALUE_VALUE_BOOL = 5;
+const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
+const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
+
+// for MessageValue Proto
+const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1;
+
+void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
+ ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
+ switch (dimensionsValue.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
+ dimensionsValue.value_str());
+ break;
+ case DimensionsValue::ValueCase::kValueInt:
+ protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
+ dimensionsValue.value_int());
+ break;
+ case DimensionsValue::ValueCase::kValueLong:
+ protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
+ dimensionsValue.value_long());
+ break;
+ case DimensionsValue::ValueCase::kValueBool:
+ protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL,
+ dimensionsValue.value_bool());
+ break;
+ case DimensionsValue::ValueCase::kValueFloat:
+ protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
+ dimensionsValue.value_float());
+ break;
+ case DimensionsValue::ValueCase::kValueTuple:
+ {
+ long long tupleToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
+ for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) {
+ long long token = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED
+ | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO);
+ writeDimensionsValueProtoToStream(
+ dimensionsValue.value_tuple().dimensions_value(i), protoOutput);
+ protoOutput->end(token);
+ }
+ protoOutput->end(tupleToken);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// for Field Proto
+const int FIELD_FIELD = 1;
+const int FIELD_POSITION_INDEX = 2;
+const int FIELD_CHILD = 3;
+
+void writeFieldProtoToStream(
+ const Field& field, util::ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
+ if (field.has_position_index()) {
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
+ }
+ for (int i = 0; i < field.child_size(); ++i) {
+ long long childToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD);
+ writeFieldProtoToStream(field.child(i), protoOutput);
+ protoOutput->end(childToken);
+ }
+}
+
+namespace {
+
+void addOrUpdateChildrenMap(
+ const Field& root,
+ const Field& node,
+ std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
+ Field parentNode = root;
+ if (node.has_position_index()) {
+ appendLeaf(&parentNode, node.field(), node.position_index());
+ } else {
+ appendLeaf(&parentNode, node.field());
+ }
+ if (childrenMap->find(parentNode) == childrenMap->end()) {
+ childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{}));
+ }
+ auto it = childrenMap->find(parentNode);
+ for (int i = 0; i < node.child_size(); ++i) {
+ auto child = node.child(i);
+ Field childNode = parentNode;
+ if (child.has_position_index()) {
+ appendLeaf(&childNode, child.field(), child.position_index());
+ } else {
+ appendLeaf(&childNode, child.field());
+ }
+ it->second.insert(childNode);
+ addOrUpdateChildrenMap(parentNode, child, childrenMap);
+ }
+}
+
+void addOrUpdateChildrenMap(
+ const Field& field,
+ std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
+ Field root;
+ addOrUpdateChildrenMap(root, field, childrenMap);
+}
+
+} // namespace
+
+void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
+ util::ProtoOutputStream* protoOutput) {
+ std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap;
+ // Rebuild the field tree.
+ for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) {
+ addOrUpdateChildrenMap(it->first, &childrenMap);
+ }
+ std::stack<std::pair<long long, Field>> tokenStack;
+ // Iterate over the node tree to fill the Atom proto.
+ for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) {
+ const Field* nodeLeaf = getSingleLeaf(&it->first);
+ const int fieldNum = nodeLeaf->field();
+ while (!tokenStack.empty()) {
+ auto currentMsgNode = tokenStack.top().second;
+ auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode);
+ if (currentMsgNodeChildrenIt->second.find(it->first) ==
+ currentMsgNodeChildrenIt->second.end()) {
+ protoOutput->end(tokenStack.top().first);
+ tokenStack.pop();
+ } else {
+ break;
+ }
+ }
+ if (it->second.size() == 0) {
+ auto itValue = fieldValueMap.find(it->first);
+ if (itValue != fieldValueMap.end()) {
+ const DimensionsValue& value = itValue->second;
+ switch (value.value_case()) {
+ case DimensionsValue::ValueCase::kValueStr:
+ protoOutput->write(FIELD_TYPE_STRING | fieldNum,
+ value.value_str());
+ break;
+ case DimensionsValue::ValueCase::kValueInt:
+ protoOutput->write(FIELD_TYPE_INT32 | fieldNum,
+ value.value_int());
+ break;
+ case DimensionsValue::ValueCase::kValueLong:
+ protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
+ value.value_long());
+ break;
+ case DimensionsValue::ValueCase::kValueBool:
+ protoOutput->write(FIELD_TYPE_BOOL | fieldNum,
+ value.value_bool());
+ break;
+ case DimensionsValue::ValueCase::kValueFloat:
+ protoOutput->write(FIELD_TYPE_FLOAT | fieldNum,
+ value.value_float());
+ break;
+ // This would not happen as the node has no child.
+ case DimensionsValue::ValueCase::kValueTuple:
+ break;
+ default:
+ break;
+ }
+ } else {
+ ALOGE("Leaf node value not found. This should never happen.");
+ }
+ } else {
+ long long token;
+ if (nodeLeaf->has_position_index()) {
+ token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
+ } else {
+ token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
+ }
+ tokenStack.push(std::make_pair(token, it->first));
+ }
+ }
+
+ while (!tokenStack.empty()) {
+ protoOutput->end(tokenStack.top().first);
+ tokenStack.pop();
+ }
+
+
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
new file mode 100644
index 0000000..1f81860
--- /dev/null
+++ b/cmds/statsd/src/stats_log_util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/util/ProtoOutputStream.h>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "field_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper function to write DimensionsValue proto to ProtoOutputStream.
+void writeDimensionsValueProtoToStream(
+ const DimensionsValue& fieldValue, util::ProtoOutputStream* protoOutput);
+
+// Helper function to write Field proto to ProtoOutputStream.
+void writeFieldProtoToStream(
+ const Field& field, util::ProtoOutputStream* protoOutput);
+
+// Helper function to construct the field value tree and write to ProtoOutputStream
+void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
+ util::ProtoOutputStream* protoOutput);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 1cdf031..e46b8eb 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -27,45 +27,15 @@
namespace os {
namespace statsd {
-const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(vector<KeyValuePair>());
+const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
// Minimum bucket size in seconds
const long kMinBucketSizeSec = 5 * 60;
-typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+typedef std::map<std::string, std::vector<HashableDimensionKey>> ConditionKey;
typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
-/*
- * In memory rep for LogEvent. Uses much less memory than LogEvent
- */
-typedef struct EventKV {
- std::vector<KeyValuePair> kv;
- string ToString() const {
- std::ostringstream result;
- result << "{ ";
- const size_t N = kv.size();
- for (size_t i = 0; i < N; i++) {
- result << " ";
- result << (i + 1);
- result << "->";
- const auto& pair = kv[i];
- if (pair.has_value_int()) {
- result << pair.value_int();
- } else if (pair.has_value_long()) {
- result << pair.value_long();
- } else if (pair.has_value_float()) {
- result << pair.value_float();
- } else if (pair.has_value_str()) {
- result << pair.value_str().c_str();
- }
- }
- result << " }";
- return result.str();
- }
-} EventKV;
-
-typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<EventKV>> DimToEventKVMap;
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 4729f6a..1ed1e05 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -22,30 +22,52 @@
option java_package = "com.android.internal.os";
option java_outer_classname = "StatsdConfigProto";
-message KeyMatcher {
- optional int32 key = 1;
-
- optional bool as_package_name = 2 [default = false];
+enum Position {
+ POSITION_UNKNOWN = 0;
+ FIRST = 1;
+ LAST = 2;
+ ANY = 3;
}
-message KeyValueMatcher {
- optional KeyMatcher key_matcher = 1;
+message FieldMatcher {
+ optional int32 field = 1;
+
+ optional Position position = 2;
+
+ repeated FieldMatcher child = 3;
+}
+
+message FieldValueMatcher {
+ // Field id, as specified in the atom proto message.
+ optional int32 field = 1;
+
+ // For repeated fields, specifies the position in the array.
+ // FIRST and LAST mean that if the values are found at the first
+ // or last position, it's a match. ANY means that if the values are found
+ // anywhere in the array, then it's a match.
+ optional Position position = 2;
oneof value_matcher {
- bool eq_bool = 2;
- string eq_string = 3;
- int32 eq_int = 4;
+ bool eq_bool = 3;
+ string eq_string = 4;
+ int32 eq_int = 5;
- int64 lt_int = 5;
- int64 gt_int = 6;
- float lt_float = 7;
- float gt_float = 8;
+ int64 lt_int = 6;
+ int64 gt_int = 7;
+ float lt_float = 8;
+ float gt_float = 9;
- int64 lte_int = 9;
- int64 gte_int = 10;
+ int64 lte_int = 10;
+ int64 gte_int = 11;
+
+ MessageMatcher matches_tuple = 12;
}
}
+message MessageMatcher {
+ repeated FieldValueMatcher field_value_matcher = 1;
+}
+
enum LogicalOperation {
LOGICAL_OPERATION_UNSPECIFIED = 0;
AND = 1;
@@ -56,9 +78,9 @@
}
message SimpleAtomMatcher {
- optional int32 tag = 1;
+ optional int32 atom_id = 1;
- repeated KeyValueMatcher key_value_matcher = 2;
+ repeated FieldValueMatcher field_value_matcher = 2;
}
message AtomMatcher {
@@ -90,7 +112,7 @@
}
optional InitialValue initial_value = 5 [default = FALSE];
- repeated KeyMatcher dimension = 6;
+ optional FieldMatcher dimensions = 6;
}
message Predicate {
@@ -115,14 +137,14 @@
message MetricConditionLink {
optional string condition = 1;
- repeated KeyMatcher key_in_what = 2;
+ optional FieldMatcher dimensions_in_what = 2;
- repeated KeyMatcher key_in_condition = 3;
+ optional FieldMatcher dimensions_in_condition = 3;
}
message FieldFilter {
- optional bool include_all = 1;
- repeated int32 field_num = 2;
+ optional bool include_all = 1 [default = false];
+ optional FieldMatcher fields = 2;
}
message EventMetric {
@@ -142,7 +164,7 @@
optional string condition = 3;
- repeated KeyMatcher dimension = 4;
+ optional FieldMatcher dimensions = 4;
optional Bucket bucket = 5;
@@ -165,7 +187,7 @@
}
optional AggregationType aggregation_type = 5 [default = SUM];
- repeated KeyMatcher dimension = 6;
+ optional FieldMatcher dimensions = 6;
optional Bucket bucket = 7;
}
@@ -175,11 +197,11 @@
optional string what = 2;
- optional FieldFilter gauge_fields = 3;
+ optional FieldFilter gauge_fields_filter = 3;
optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ optional FieldMatcher dimensions = 5;
optional Bucket bucket = 6;
@@ -195,7 +217,7 @@
optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ optional FieldMatcher dimensions = 5;
optional Bucket bucket = 6;
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 1ec9155..111b4ba 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -32,65 +32,313 @@
const int FIELD_ID_2 = 2;
const int FIELD_ID_3 = 2;
+const int ATTRIBUTION_UID_FIELD_ID = 1;
+const int ATTRIBUTION_TAG_FIELD_ID = 2;
+
// Private API from liblog.
extern "C" void android_log_rewind(android_log_context ctx);
#ifdef __ANDROID__
TEST(AtomMatcherTest, TestSimpleMatcher) {
+ UidMap uidMap;
+
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
+ simpleMatcher->set_atom_id(TAG_ID);
LogEvent event(TAG_ID, 0);
+ EXPECT_TRUE(event.write(11));
event.init();
// Test
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Wrong tag id.
+ simpleMatcher->set_atom_id(TAG_ID + 1);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
-TEST(AtomMatcherTest, TestBoolMatcher) {
- // Set up the matcher
- AtomMatcher matcher;
- auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
- auto keyValue1 = simpleMatcher->add_key_value_matcher();
- keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
- auto keyValue2 = simpleMatcher->add_key_value_matcher();
- keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+TEST(AtomMatcherTest, TestAttributionMatcher) {
+ UidMap uidMap;
+ AttributionNode attribution_node1;
+ attribution_node1.set_uid(1111);
+ attribution_node1.set_tag("location1");
+
+ AttributionNode attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("location2");
+
+ AttributionNode attribution_node3;
+ attribution_node3.set_uid(3333);
+ attribution_node3.set_tag("location3");
+ std::vector<AttributionNode> attribution_nodes =
+ { attribution_node1, attribution_node2, attribution_node3 };
// Set up the event
LogEvent event(TAG_ID, 0);
- event.write(true);
- event.write(false);
+ event.write(attribution_nodes);
+ event.write("some value");
+ // Convert to a LogEvent
+ event.init();
+
+ // Set up the matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+
+ // Match first node.
+ auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+ attributionMatcher->set_field(FIELD_ID_1);
+ attributionMatcher->set_position(Position::FIRST);
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+ ATTRIBUTION_TAG_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag");
+
+ auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+ fieldMatcher->set_field(FIELD_ID_2);
+ fieldMatcher->set_eq_string("some value");
+
+ // Tag not matched.
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match last node.
+ attributionMatcher->set_position(Position::LAST);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Match any node.
+ attributionMatcher->set_position(Position::ANY);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location4");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Attribution match but primitive field not match.
+ attributionMatcher->set_position(Position::ANY);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("location2");
+ fieldMatcher->set_eq_string("wrong value");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ fieldMatcher->set_eq_string("some value");
+
+ // Uid match.
+ attributionMatcher->set_position(Position::ANY);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
+ ATTRIBUTION_UID_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ uidMap.updateMap({1111, 1111, 2222, 3333, 3333} /* uid list */,
+ {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("pkg0"), android::String16("pkg1"),
+ android::String16("pkg1"), android::String16("Pkg2"),
+ android::String16("PkG3")} /* package name list */);
+
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::FIRST);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::LAST);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ // Uid + tag.
+ attributionMatcher->set_position(Position::ANY);
+ attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+ ATTRIBUTION_TAG_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location2");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::FIRST);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+ attributionMatcher->set_position(Position::LAST);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location3");
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+ ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+ ->set_eq_string("location1");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestBoolMatcher) {
+ UidMap uidMap;
+ // Set up the matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ auto keyValue1 = simpleMatcher->add_field_value_matcher();
+ keyValue1->set_field(FIELD_ID_1);
+ auto keyValue2 = simpleMatcher->add_field_value_matcher();
+ keyValue2->set_field(FIELD_ID_2);
+
+ // Set up the event
+ LogEvent event(TAG_ID, 0);
+ EXPECT_TRUE(event.write(true));
+ EXPECT_TRUE(event.write(false));
// Convert to a LogEvent
event.init();
// Test
keyValue1->set_eq_bool(true);
keyValue2->set_eq_bool(false);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue1->set_eq_bool(false);
keyValue2->set_eq_bool(false);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- keyValue1->set_eq_bool(true);
- keyValue2->set_eq_bool(false);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ keyValue1->set_eq_bool(false);
+ keyValue2->set_eq_bool(true);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue1->set_eq_bool(true);
keyValue2->set_eq_bool(true);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
TEST(AtomMatcherTest, TestStringMatcher) {
+ UidMap uidMap;
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
- auto keyValue = simpleMatcher->add_key_value_matcher();
- keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+ simpleMatcher->set_atom_id(TAG_ID);
+ auto keyValue = simpleMatcher->add_field_value_matcher();
+ keyValue->set_field(FIELD_ID_1);
keyValue->set_eq_string("some value");
// Set up the event
@@ -100,18 +348,19 @@
event.init();
// Test
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
}
TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
+ UidMap uidMap;
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
- auto keyValue1 = simpleMatcher->add_key_value_matcher();
- keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
- auto keyValue2 = simpleMatcher->add_key_value_matcher();
- keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+ simpleMatcher->set_atom_id(TAG_ID);
+ auto keyValue1 = simpleMatcher->add_field_value_matcher();
+ keyValue1->set_field(FIELD_ID_1);
+ auto keyValue2 = simpleMatcher->add_field_value_matcher();
+ keyValue2->set_field(FIELD_ID_2);
// Set up the event
LogEvent event(TAG_ID, 0);
@@ -124,25 +373,26 @@
// Test
keyValue1->set_eq_int(2);
keyValue2->set_eq_int(3);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue1->set_eq_int(2);
keyValue2->set_eq_int(4);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue1->set_eq_int(4);
keyValue2->set_eq_int(3);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
TEST(AtomMatcherTest, TestIntComparisonMatcher) {
+ UidMap uidMap;
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
- auto keyValue = simpleMatcher->add_key_value_matcher();
- keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+ simpleMatcher->set_atom_id(TAG_ID);
+ auto keyValue = simpleMatcher->add_field_value_matcher();
+ keyValue->set_field(FIELD_ID_1);
// Set up the event
LogEvent event(TAG_ID, 0);
@@ -153,82 +403,83 @@
// eq_int
keyValue->set_eq_int(10);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_eq_int(11);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_eq_int(12);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// lt_int
keyValue->set_lt_int(10);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_lt_int(11);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_lt_int(12);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
// lte_int
keyValue->set_lte_int(10);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_lte_int(11);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_lte_int(12);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
// gt_int
keyValue->set_gt_int(10);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_gt_int(11);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_gt_int(12);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// gte_int
keyValue->set_gte_int(10);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_gte_int(11);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
keyValue->set_gte_int(12);
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
+ UidMap uidMap;
// Set up the matcher
AtomMatcher matcher;
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
- simpleMatcher->set_tag(TAG_ID);
+ simpleMatcher->set_atom_id(TAG_ID);
- auto keyValue = simpleMatcher->add_key_value_matcher();
- keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+ auto keyValue = simpleMatcher->add_field_value_matcher();
+ keyValue->set_field(FIELD_ID_1);
LogEvent event1(TAG_ID, 0);
keyValue->set_lt_float(10.0);
event1.write(10.1f);
event1.init();
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event1));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
LogEvent event2(TAG_ID, 0);
event2.write(9.9f);
event2.init();
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event2));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
LogEvent event3(TAG_ID, 0);
event3.write(10.1f);
event3.init();
keyValue->set_gt_float(10.0);
- EXPECT_TRUE(matchesSimple(*simpleMatcher, event3));
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
LogEvent event4(TAG_ID, 0);
event4.write(9.9f);
event4.init();
- EXPECT_FALSE(matchesSimple(*simpleMatcher, event4));
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
}
// Helper for the composite matchers.
void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
- simpleMatcher->set_tag(tag);
- auto keyValue = simpleMatcher->add_key_value_matcher();
- keyValue->mutable_key_matcher()->set_key(key);
+ simpleMatcher->set_atom_id(tag);
+ auto keyValue = simpleMatcher->add_field_value_matcher();
+ keyValue->set_field(key);
keyValue->set_eq_int(val);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
new file mode 100644
index 0000000..fd28460
--- /dev/null
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -0,0 +1,573 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+#include <log/log_event_list.h>
+#include "src/logd/LogEvent.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(LogEventTest, testEmptyEvent) {
+ const int32_t TAG_ID = 123;
+ LogEvent event(TAG_ID, 0);
+ event.init();
+
+ DimensionsValue dimensionsValue;
+ EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue));
+ FieldMatcher dimensions;
+ dimensions.set_field(event.GetTagId());
+ EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+ dimensions.add_child()->set_field(3);
+ dimensions.mutable_child(0)->set_position(Position::FIRST);
+ EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+ dimensions.mutable_child(0)->set_position(Position::ANY);
+ EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+ dimensions.mutable_child(0)->set_position(Position::LAST);
+ EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+}
+
+TEST(LogEventTest, testRepeatedAttributionNode) {
+ const int32_t TAG_ID = 123;
+ LogEvent event(TAG_ID, 0);
+ AttributionNode attribution_node1;
+ attribution_node1.set_uid(1111);
+ attribution_node1.set_tag("locationService");
+
+ AttributionNode attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("locationService2");
+
+ AttributionNode attribution_node3;
+ attribution_node3.set_uid(3333);
+ attribution_node3.set_tag("locationService3");
+ std::vector<AttributionNode> attribution_nodes =
+ {attribution_node1, attribution_node2, attribution_node3};
+
+ // 1nd field: int32.
+ EXPECT_TRUE(event.write(int32_t(11)));
+ // 2rd field: float.
+ EXPECT_TRUE(event.write(3.45f));
+ // Here it assume that the atom proto contains a repeated AttributionNode field.
+ // 3rd field: attribution node. This is repeated field.
+ EXPECT_TRUE(event.write(attribution_nodes));
+ // 4th field: bool.
+ EXPECT_TRUE(event.write(true));
+ // 5th field: long.
+ EXPECT_TRUE(event.write(uint64_t(1234)));
+
+ event.init();
+
+ DimensionsValue dimensionsValue;
+ // Query single primitive fields.
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+ // The bool value is stored in value_int field as logD does not support bool.
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+ // First attribution.
+ FieldMatcher first_uid_dimensions;
+ first_uid_dimensions.set_field(event.GetTagId());
+ first_uid_dimensions.add_child()->set_field(3);
+ first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST);
+ first_uid_dimensions.mutable_child(0)->add_child()->set_field(1);
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+
+ FieldMatcher first_tag_dimensions = first_uid_dimensions;
+ first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2);
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_str(), "locationService");
+
+ FieldMatcher first_attribution_dimensions = first_uid_dimensions;
+ first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2);
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService");
+
+ FieldMatcher last_attribution_dimensions = first_attribution_dimensions;
+ last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST);
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 3333);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService3");
+
+ FieldMatcher any_attribution_dimensions = first_attribution_dimensions;
+ any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY);
+ std::vector<DimensionsValue> dimensionsValues;
+ event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues);
+ EXPECT_EQ(dimensionsValues.size(), 3u);
+ EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService");
+ EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 2222);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService2");
+ EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 3333);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService3");
+
+ FieldMatcher mixed_dimensions = any_attribution_dimensions;
+ mixed_dimensions.add_child()->set_field(1000);
+ mixed_dimensions.add_child()->set_field(6); // missing field.
+ mixed_dimensions.add_child()->set_field(3); // position not set.
+ mixed_dimensions.add_child()->set_field(5);
+ mixed_dimensions.add_child()->set_field(1);
+ dimensionsValues.clear();
+ event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues);
+ EXPECT_EQ(dimensionsValues.size(), 3u);
+ EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+ 1111);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+ "locationService");
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234));
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1);
+ EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11);
+
+ EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+ 2222);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+ "locationService2");
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234));
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1);
+ EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11);
+
+ EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+ 3333);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+ "locationService3");
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234));
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1);
+ EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11);
+
+ FieldMatcher wrong_dimensions = mixed_dimensions;
+ // Wrong tagId.
+ wrong_dimensions.set_field(event.GetTagId() + 100);
+ dimensionsValues.clear();
+ event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues);
+ EXPECT_TRUE(dimensionsValues.empty());
+}
+
+TEST(LogEventTest, testMessageField) {
+ const int32_t TAG_ID = 123;
+ LogEvent event(TAG_ID, 0);
+ AttributionNode attribution_node1;
+ attribution_node1.set_uid(1111);
+ attribution_node1.set_tag("locationService");
+
+ AttributionNode attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("locationService2");
+
+ // 1nd field: int32.
+ EXPECT_TRUE(event.write(int32_t(11)));
+ // 2rd field: float.
+ EXPECT_TRUE(event.write(3.45f));
+ // Here it assume that the atom proto contains two optional AttributionNode fields.
+ // 3rd field: attribution node. This is not repeated field.
+ EXPECT_TRUE(event.write(attribution_node1));
+ // 4th field: another attribution field. This is not repeated field.
+ EXPECT_TRUE(event.write(attribution_node2));
+ // 5th field: bool.
+ EXPECT_TRUE(event.write(true));
+ // 6th field: long.
+ EXPECT_TRUE(event.write(uint64_t(1234)));
+
+ event.init();
+
+ FieldMatcher uid_dimensions1;
+ uid_dimensions1.set_field(event.GetTagId());
+ uid_dimensions1.add_child()->set_field(3);
+ uid_dimensions1.mutable_child(0)->add_child()->set_field(1);
+
+ FieldMatcher tag_dimensions1;
+ tag_dimensions1.set_field(event.GetTagId());
+ tag_dimensions1.add_child()->set_field(3);
+ tag_dimensions1.mutable_child(0)->add_child()->set_field(2);
+
+ FieldMatcher attribution_dimensions1;
+ attribution_dimensions1.set_field(event.GetTagId());
+ attribution_dimensions1.add_child()->set_field(3);
+ attribution_dimensions1.mutable_child(0)->add_child()->set_field(1);
+ attribution_dimensions1.mutable_child(0)->add_child()->set_field(2);
+
+ FieldMatcher uid_dimensions2 = uid_dimensions1;
+ uid_dimensions2.mutable_child(0)->set_field(4);
+
+ FieldMatcher tag_dimensions2 = tag_dimensions1;
+ tag_dimensions2.mutable_child(0)->set_field(4);
+
+ FieldMatcher attribution_dimensions2 = attribution_dimensions1;
+ attribution_dimensions2.mutable_child(0)->set_field(4);
+
+ FieldMatcher mixed_dimensions = attribution_dimensions1;
+ mixed_dimensions.add_child()->set_field(4);
+ mixed_dimensions.mutable_child(1)->add_child()->set_field(1);
+ mixed_dimensions.add_child()->set_field(1000);
+ mixed_dimensions.add_child()->set_field(5);
+ mixed_dimensions.add_child()->set_field(1);
+
+ DimensionsValue dimensionsValue;
+
+ // Query single primitive fields.
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+ // The bool value is stored in value_int field as logD does not support bool.
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+ // Query atom field 3: attribution node uid field only.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+
+ // Query atom field 3: attribution node tag field only.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_str(), "locationService");
+
+ // Query atom field 3: attribution node uid + tag fields.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService");
+
+ // Query atom field 4: attribution node uid field only.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 2222);
+
+ // Query atom field 4: attribution node tag field only.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_str(), "locationService2");
+
+ // Query atom field 4: attribution node uid + tag fields.
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 2222);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService2");
+
+ // Query multiple fields:
+ // 1/ Field 3: attribution uid + tag.
+ // 2/ Field 4: attribution uid only.
+ // 3/ Field not exist.
+ // 4/ Primitive fields #5
+ // 5/ Primitive fields #1
+ EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value_size(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(0).value_int(), 1111);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+ .dimensions_value(1).value_str(), "locationService");
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+ .dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+ .dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+ .dimensions_value(0).value_int(), 2222);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5);
+ // The bool value is stored in value_int field as logD does not support bool.
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11);
+}
+
+TEST(LogEventTest, testAllPrimitiveFields) {
+ const int32_t TAG_ID = 123;
+ LogEvent event(TAG_ID, 0);
+
+ // 1nd field: int32.
+ EXPECT_TRUE(event.write(int32_t(11)));
+ // 2rd field: float.
+ EXPECT_TRUE(event.write(3.45f));
+ // 3th field: string.
+ EXPECT_TRUE(event.write("test"));
+ // 4th field: bool.
+ EXPECT_TRUE(event.write(true));
+ // 5th field: bool.
+ EXPECT_TRUE(event.write(false));
+ // 6th field: long.
+ EXPECT_TRUE(event.write(uint64_t(1234)));
+
+ event.init();
+
+ DimensionsValue dimensionsValue;
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test");
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+ // The bool value is stored in value_int field as logD does not support bool.
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+ // The bool value is stored in value_int field as logD does not support bool.
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false);
+
+ EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
+ EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
+ EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+ // Field not exist.
+ EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue));
+}
+
+TEST(LogEventTest, testWriteAtomProtoToStream) {
+ AttributionNode attribution_node1;
+ attribution_node1.set_uid(1111);
+ attribution_node1.set_tag("locationService");
+
+ AttributionNode attribution_node2;
+ attribution_node2.set_uid(2222);
+ attribution_node2.set_tag("locationService2");
+
+ AttributionNode attribution_node3;
+ attribution_node3.set_uid(3333);
+ attribution_node3.set_tag("locationService3");
+ std::vector<AttributionNode> attribution_nodes =
+ {attribution_node1, attribution_node2, attribution_node3};
+
+ LogEvent event(1, 0);
+ EXPECT_TRUE(event.write("222"));
+ EXPECT_TRUE(event.write(attribution_nodes));
+ EXPECT_TRUE(event.write(345));
+ EXPECT_TRUE(event.write(attribution_node3));
+ EXPECT_TRUE(event.write("hello"));
+ event.init();
+
+ util::ProtoOutputStream protoOutput;
+ // For now only see whether it will crash.
+ // TODO(yanglu): test parsing from stream.
+ event.ToProto(protoOutput);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
\ No newline at end of file
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 3c8ccab..6cd31dd 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -52,20 +52,20 @@
eventMatcher->set_name("SCREEN_IS_ON");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("SCREEN_IS_OFF");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
eventMatcher = config.add_atom_matcher();
@@ -80,8 +80,8 @@
metric->set_name("3");
metric->set_what("SCREEN_IS_ON");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- KeyMatcher* keyMatcher = metric->add_dimension();
- keyMatcher->set_key(1);
+ metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions()->add_child()->set_field(1);
auto alert = config.add_alert();
alert->set_name("3");
@@ -100,10 +100,10 @@
eventMatcher->set_name("SCREEN_IS_ON");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
eventMatcher = config.add_atom_matcher();
@@ -129,8 +129,8 @@
metric->set_name("3");
metric->set_what("SCREEN_IS_ON");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- KeyMatcher* keyMatcher = metric->add_dimension();
- keyMatcher->set_key(1);
+ metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+ metric->mutable_dimensions()->add_child()->set_field(1);
auto alert = config.add_alert();
alert->set_name("3");
@@ -149,10 +149,10 @@
eventMatcher->set_name("SCREEN_IS_ON");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
eventMatcher = config.add_atom_matcher();
@@ -181,7 +181,7 @@
eventMatcher->set_name("SCREEN_EVENT");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2);
+ simpleAtomMatcher->set_atom_id(2);
return config;
}
@@ -193,12 +193,12 @@
AtomMatcher* eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("BATTERY_VERY_LOW");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2);
+ simpleAtomMatcher->set_atom_id(2);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(3);
+ simpleAtomMatcher->set_atom_id(3);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("BATTERY_LOW");
@@ -213,8 +213,8 @@
metric->set_name("3");
metric->set_what("BATTERY_LOW");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
- KeyMatcher* keyMatcher = metric->add_dimension();
- keyMatcher->set_key(1);
+ // This case is interesting. We want to dimension across two atoms.
+ metric->mutable_dimensions()->add_child()->set_field(1);
auto alert = config.add_alert();
alert->set_name("3");
@@ -233,20 +233,20 @@
eventMatcher->set_name("SCREEN_IS_ON");
SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
eventMatcher = config.add_atom_matcher();
eventMatcher->set_name("SCREEN_IS_OFF");
simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
- simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+ simpleAtomMatcher->add_field_value_matcher()->set_field(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
auto condition = config.add_predicate();
@@ -267,6 +267,7 @@
}
TEST(MetricsManagerTest, TestGoodConfig) {
+ UidMap uidMap;
StatsdConfig config = buildGoodConfig();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -277,7 +278,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_TRUE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
EXPECT_EQ(1u, allMetricProducers.size());
@@ -285,6 +286,7 @@
}
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+ UidMap uidMap;
StatsdConfig config = buildDimensionMetricsWithMultiTags();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -295,12 +297,13 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+ UidMap uidMap;
StatsdConfig config = buildCircleMatchers();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -311,12 +314,13 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
+ UidMap uidMap;
StatsdConfig config = buildMissingMatchers();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -326,12 +330,13 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
+ UidMap uidMap;
StatsdConfig config = buildMissingPredicate();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -341,12 +346,13 @@
unordered_map<int, std::vector<int>> conditionToMetricMap;
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+ UidMap uidMap;
StatsdConfig config = buildCirclePredicates();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -357,12 +363,13 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+ UidMap uidMap;
StatsdConfig config = buildAlertWithUnknownMetric();
set<int> allTagIds;
vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -373,7 +380,7 @@
unordered_map<int, std::vector<int>> trackerToMetricMap;
unordered_map<int, std::vector<int>> trackerToConditionMap;
- EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+ EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
allConditionTrackers, allMetricProducers, allAnomalyTrackers,
conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
}
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 9b96bb7..a27bed5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -52,7 +52,7 @@
sp<UidMap> m = new UidMap();
sp<AnomalyMonitor> anomalyMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
+ StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
MockMetricsManager mockMetricsManager;
@@ -69,7 +69,7 @@
sp<UidMap> m = new UidMap();
sp<AnomalyMonitor> anomalyMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyMonitor,
+ StatsLogProcessor p(m, anomalyMonitor, 0,
[&broadcastCount](const ConfigKey& key) { broadcastCount++; });
MockMetricsManager mockMetricsManager;
@@ -93,7 +93,7 @@
sp<UidMap> m = new UidMap();
sp<AnomalyMonitor> anomalyMonitor;
int broadcastCount = 0;
- StatsLogProcessor p(m, anomalyMonitor,
+ StatsLogProcessor p(m, anomalyMonitor, 0,
[&broadcastCount](const ConfigKey& key) { broadcastCount++; });
MockMetricsManager mockMetricsManager;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 3fa96d3..8a394f7 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -37,7 +37,7 @@
sp<UidMap> m = new UidMap();
sp<AnomalyMonitor> anomalyMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
+ StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
addEvent.write(100); // parent UID
addEvent.write(101); // isolated UID
@@ -273,4 +273,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index a016054..da872ad 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -34,14 +34,10 @@
const ConfigKey kConfigKey(0, "test");
HashableDimensionKey getMockDimensionKey(int key, string value) {
- KeyValuePair pair;
- pair.set_key(key);
- pair.set_value_str(value);
-
- vector<KeyValuePair> pairs;
- pairs.push_back(pair);
-
- return HashableDimensionKey(pairs);
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(key);
+ dimensionsValue.set_value_str(value);
+ return HashableDimensionKey(dimensionsValue);
}
void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index eb0fafe..705804f 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include <stdio.h>
#include <vector>
+#include <numeric>
using std::map;
using std::unordered_map;
@@ -30,15 +31,22 @@
const ConfigKey kConfigKey(0, "test");
+const int ATTRIBUTION_NODE_FIELD_ID = 1;
+const int ATTRIBUTION_UID_FIELD_ID = 1;
+const int TAG_ID = 1;
+
SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
- bool outputSlicedUid) {
+ bool outputSlicedUid, Position position) {
SimplePredicate simplePredicate;
simplePredicate.set_start("WAKE_LOCK_ACQUIRE");
simplePredicate.set_stop("WAKE_LOCK_RELEASE");
simplePredicate.set_stop_all("RELEASE_ALL");
if (outputSlicedUid) {
- KeyMatcher* keyMatcher = simplePredicate.add_dimension();
- keyMatcher->set_key(1);
+ simplePredicate.mutable_dimensions()->set_field(TAG_ID);
+ simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+ simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position);
+ simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field(
+ ATTRIBUTION_UID_FIELD_ID);
}
simplePredicate.set_count_nesting(countNesting);
@@ -47,24 +55,55 @@
return simplePredicate;
}
-void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
- event->write(uid); // uid
+void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
+ std::vector<AttributionNode> nodes;
+ for (size_t i = 0; i < uids.size(); ++i) {
+ AttributionNode node;
+ node.set_uid(uids[i]);
+ nodes.push_back(node);
+ }
+ event->write(nodes); // attribution chain.
+}
+
+void makeWakeLockEvent(
+ LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
+ writeAttributionNodesToEvent(event, uids);
event->write(wl);
event->write(acquire);
event->init();
}
-map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
- const string& conditionName) {
- // test query
- KeyValuePair kv1;
- kv1.set_key(key);
- kv1.set_value_int(uid);
- vector<KeyValuePair> kv_list;
- kv_list.push_back(kv1);
- map<string, HashableDimensionKey> queryKey;
- queryKey[conditionName] = HashableDimensionKey(kv_list);
- return queryKey;
+std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
+ const Position position,
+ const std::vector<int> &uids, const string& conditionName) {
+ std::map<string, std::vector<HashableDimensionKey>> outputKeyMap;
+ std::vector<int> uid_indexes;
+ switch(position) {
+ case Position::FIRST:
+ uid_indexes.push_back(0);
+ break;
+ case Position::LAST:
+ uid_indexes.push_back(uids.size() - 1);
+ break;
+ case Position::ANY:
+ uid_indexes.resize(uids.size());
+ std::iota(uid_indexes.begin(), uid_indexes.end(), 0);
+ break;
+ default:
+ break;
+ }
+
+ for (const int idx : uid_indexes) {
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(TAG_ID);
+ dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
+ ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
+ ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]);
+ outputKeyMap[conditionName].push_back(HashableDimensionKey(dimensionsValue));
+ }
+ return outputKeyMap;
}
TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
@@ -221,91 +260,115 @@
}
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
- SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
- string conditionName = "WL_HELD_BY_UID2";
+ for (Position position :
+ { Position::ANY, Position::FIRST, Position::LAST}) {
+ SimplePredicate simplePredicate = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+ position);
+ string conditionName = "WL_HELD_BY_UID2";
- unordered_map<string, int> trackerNameIndexMap;
- trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
- trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
- trackerNameIndexMap["RELEASE_ALL"] = 2;
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
- SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
- int uid = 111;
+ SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+ 0 /*condition tracker index*/, simplePredicate,
+ trackerNameIndexMap);
+ std::vector<int> uids = {111, 222, 333};
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uid, "wl1", 1);
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uids, "wl1", 1);
- // one matched start
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allPredicates;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
+ conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ changedCache);
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
+ if (position == Position::FIRST ||
+ position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(changedCache[0]);
- // Now test query
- const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- // another wake lock acquired by this uid
- LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uid, "wl2", 1);
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_FALSE(changedCache[0]);
+ // another wake lock acquired by this uid
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uids, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST ||
+ position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
- // wake lock 1 release
- LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event3, uid, "wl1", 0); // now release it.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
- changedCache);
- // nothing changes, because wake lock 2 is still held for this uid
- EXPECT_FALSE(changedCache[0]);
+ // wake lock 1 release
+ LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event3, uids, "wl1", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+ changedCache);
+ // nothing changes, because wake lock 2 is still held for this uid
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST ||
+ position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
- LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event4, uid, "wl2", 0); // now release it.
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
+ LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
- // query again
- conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+ }
+
}
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+ true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
+ Position::ANY /* position */);
string conditionName = "WL_HELD";
unordered_map<string, int> trackerNameIndexMap;
@@ -316,13 +379,14 @@
SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
- int uid1 = 111;
+
+ std::vector<int> uid_list1 = {111, 1111, 11111};
string uid1_wl1 = "wl1_1";
- int uid2 = 222;
+ std::vector<int> uid_list2 = {222, 2222, 22222};
string uid2_wl1 = "wl2_1";
LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+ makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1);
// one matched start for uid1
vector<MatchingState> matcherState;
@@ -340,7 +404,7 @@
EXPECT_TRUE(changedCache[0]);
// Now test query
- map<string, HashableDimensionKey> queryKey;
+ ConditionKey queryKey;
conditionCache[0] = ConditionState::kNotEvaluated;
conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
@@ -348,7 +412,7 @@
// another wake lock acquired by this uid
LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+ makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1);
matcherState.clear();
matcherState.push_back(MatchingState::kMatched);
matcherState.push_back(MatchingState::kNotMatched);
@@ -360,7 +424,7 @@
// uid1 wake lock 1 release
LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event3, uid1, uid1_wl1, 0); // now release it.
+ makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0); // now release it.
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -372,7 +436,7 @@
EXPECT_FALSE(changedCache[0]);
LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event4, uid2, uid2_wl1, 0); // now release it.
+ makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0); // now release it.
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -390,95 +454,111 @@
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
- SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
- string conditionName = "WL_HELD_BY_UID3";
+ for (Position position :
+ {Position::ANY, Position::FIRST, Position::LAST}) {
+ SimplePredicate simplePredicate = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+ position);
+ string conditionName = "WL_HELD_BY_UID3";
- unordered_map<string, int> trackerNameIndexMap;
- trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
- trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
- trackerNameIndexMap["RELEASE_ALL"] = 2;
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
- SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
- 0 /*condition tracker index*/, simplePredicate,
- trackerNameIndexMap);
- int uid1 = 111;
- int uid2 = 222;
+ SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+ 0 /*condition tracker index*/, simplePredicate,
+ trackerNameIndexMap);
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uid1, "wl1", 1);
+ std::vector<int> uid_list1 = {111, 1111, 11111};
+ std::vector<int> uid_list2 = {222, 2222, 22222};
- // one matched start
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid_list1, "wl1", 1);
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
- changedCache);
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allPredicates;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
+ conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ changedCache);
+ if (position == Position::FIRST ||
+ position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(changedCache[0]);
- // Now test query
- const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- // another wake lock acquired by uid2
- LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uid2, "wl2", 1);
- matcherState.clear();
- matcherState.push_back(MatchingState::kMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
- EXPECT_TRUE(changedCache[0]);
+ // another wake lock acquired by uid2
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid_list2, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+ changedCache);
+ if (position == Position::FIRST ||
+ position == Position::LAST) {
+ EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uid_list1.size() + uid_list2.size(),
+ conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(changedCache[0]);
- // TEST QUERY
- const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
+ // TEST QUERY
+ const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
- // stop all event
- LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
- matcherState.clear();
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kNotMatched);
- matcherState.push_back(MatchingState::kMatched);
+ // stop all event
+ LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
- conditionCache[0] = ConditionState::kNotEvaluated;
- changedCache[0] = false;
- conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
- changedCache);
- EXPECT_TRUE(changedCache[0]);
- EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_TRUE(changedCache[0]);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
- // TEST QUERY
- const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
+ // TEST QUERY
+ const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
- // TEST QUERY
- const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
- conditionCache[0] = ConditionState::kNotEvaluated;
+ // TEST QUERY
+ const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
- EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ }
+
}
} // namespace statsd
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
new file mode 100644
index 0000000..c747016
--- /dev/null
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -0,0 +1,228 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+StatsdConfig CreateStatsdConfig() {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = appCrashMatcher;
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(
+ android::util::SYNC_STATE_CHANGED, {1 /* uid field */});
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_name("combinationPredicate");
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate);
+
+ auto countMetric = config.add_count_metric();
+ countMetric->set_name("AppCrashes");
+ countMetric->set_what(appCrashMatcher.name());
+ countMetric->set_condition(combinationPredicate->name());
+ // The metric is dimensioning by uid only.
+ *countMetric->mutable_dimensions() =
+ CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
+ countMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = countMetric->add_links();
+ links->set_condition(isSyncingPredicate.name());
+ auto dimensionWhat = links->mutable_dimensions_in_what();
+ dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ auto dimensionCondition = links->mutable_dimensions_in_condition();
+ dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
+
+ // Links between crash atom and condition of app is in background.
+ links = countMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.name());
+ dimensionWhat = links->mutable_dimensions_in_what();
+ dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ dimensionCondition = links->mutable_dimensions_in_condition();
+ dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->add_child()->set_field(1); // uid field.
+ return config;
+}
+
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
+ auto config = CreateStatsdConfig();
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs = config.count_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+
+ ConfigKey cfgKey;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ int appUid = 123;
+ auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
+ auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
+ auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
+
+ auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
+ auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
+ auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
+
+ auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
+ auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
+
+ auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
+ auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
+
+ auto screenTurnedOnEvent =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 2);
+ auto screenTurnedOffEvent =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200);
+ auto screenTurnedOnEvent2 =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON,
+ bucketStartTimeNs + 2 * bucketSizeNs - 100);
+
+ auto syncOnEvent1 =
+ CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+ auto syncOffEvent1 =
+ CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+ auto syncOnEvent2 =
+ CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+
+ auto moveToBackgroundEvent1 =
+ CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
+ auto moveToForegroundEvent1 =
+ CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
+
+ auto moveToBackgroundEvent2 =
+ CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
+ auto moveToForegroundEvent2 =
+ CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
+
+ /*
+ bucket #1 bucket #2
+
+
+ | | | | | | | | | | (crashEvents)
+ |-------------------------------------|-----------------------------------|---------
+
+ | | (MoveToBkground)
+
+ | | (MoveToForeground)
+
+ | | (SyncIsOn)
+ | (SyncIsOff)
+ | | (ScreenIsOn)
+ | (ScreenIsOff)
+ */
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(std::move(crashEvent1));
+ events.push_back(std::move(crashEvent2));
+ events.push_back(std::move(crashEvent3));
+ events.push_back(std::move(crashEvent4));
+ events.push_back(std::move(crashEvent5));
+ events.push_back(std::move(crashEvent6));
+ events.push_back(std::move(crashEvent7));
+ events.push_back(std::move(crashEvent8));
+ events.push_back(std::move(crashEvent9));
+ events.push_back(std::move(crashEvent10));
+ events.push_back(std::move(screenTurnedOnEvent));
+ events.push_back(std::move(screenTurnedOffEvent));
+ events.push_back(std::move(screenTurnedOnEvent2));
+ events.push_back(std::move(syncOnEvent1));
+ events.push_back(std::move(syncOffEvent1));
+ events.push_back(std::move(syncOnEvent2));
+ events.push_back(std::move(moveToBackgroundEvent1));
+ events.push_back(std::move(moveToForegroundEvent1));
+ events.push_back(std::move(moveToBackgroundEvent2));
+ events.push_back(std::move(moveToForegroundEvent2));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(*event);
+ }
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+ auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+ // Validate dimension value.
+ EXPECT_EQ(data.dimension().field(),
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ // Uid field.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
+ data = reports.reports(0).metrics(0).count_metrics().data(0);
+ // Validate dimension value.
+ EXPECT_EQ(data.dimension().field(),
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ // Uid field.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
new file mode 100644
index 0000000..8d7b2d5
--- /dev/null
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -0,0 +1,178 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by any attribution node and both by uid and tag.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidAndTagDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_name("WakelockDuration");
+ durationMetric->set_what(holdingWakelockPredicate.name());
+ durationMetric->set_condition(screenIsOffPredicate.name());
+ durationMetric->set_aggregation_type(aggregationType);
+ // The metric is dimensioning by first attribution node and only by uid.
+ *durationMetric->mutable_dimensions() =
+ CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+ return config;
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
+ ConfigKey cfgKey;
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) {
+ auto config = CreateStatsdConfig(aggregationType);
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ config.duration_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ auto screenTurnedOnEvent =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 1);
+ auto screenTurnedOffEvent =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200);
+ auto screenTurnedOnEvent2 =
+ CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON,
+ bucketStartTimeNs + bucketSizeNs + 500);
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(111, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ auto acquireEvent1 = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 2);
+ auto acquireEvent2 = CreateAcquireWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
+
+ auto releaseEvent1 = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+ auto releaseEvent2 = CreateReleaseWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15);
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(std::move(screenTurnedOnEvent));
+ events.push_back(std::move(screenTurnedOffEvent));
+ events.push_back(std::move(screenTurnedOnEvent2));
+ events.push_back(std::move(acquireEvent1));
+ events.push_back(std::move(acquireEvent2));
+ events.push_back(std::move(releaseEvent1));
+ events.push_back(std::move(releaseEvent2));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(*event);
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ EXPECT_EQ(data.dimension().field(),
+ android::util::WAKELOCK_STATE_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ // Attribution field.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+ // Uid only.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value(0).value_int(), 111);
+ // Validate bucket info.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // The wakelock holding interval starts from the screen off event and to the end of the 1st
+ // bucket.
+ EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
+
+ reports.Clear();
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ // Dump the report after the end of 2nd bucket.
+ EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+ data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ EXPECT_EQ(data.dimension().field(),
+ android::util::WAKELOCK_STATE_CHANGED);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+ // Attribution field.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+ // Uid only.
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+ .value_tuple().dimensions_value(0).value_int(), 111);
+ // Two output buckets.
+ // The wakelock holding interval in the 1st bucket starts from the screen off event and to
+ // the end of the 1st bucket.
+ EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
+ bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
+ // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
+ // ends at the second screen on event.
+ EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+ }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d3269ed..6e114a6 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/metrics/CountMetricProducer.h"
+#include "src/dimension.h"
#include "metrics_test_helper.h"
#include <gmock/gmock.h>
@@ -137,26 +138,29 @@
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int tagId = 1;
+ int conditionTagId = 2;
+
CountMetric metric;
metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
MetricConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
- link->add_key_in_what()->set_key(1);
- link->add_key_in_condition()->set_key(2);
+ *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+ *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
- LogEvent event1(1, bucketStartTimeNs + 1);
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
event1.write("111"); // uid
event1.init();
ConditionKey key1;
- key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
+ key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
- LogEvent event2(1, bucketStartTimeNs + 10);
+ LogEvent event2(tagId, bucketStartTimeNs + 10);
event2.write("222"); // uid
event2.init();
ConditionKey key2;
- key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
+ key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 58a4ac6..8ee94c7 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -52,9 +52,10 @@
LogEvent event1(tagId, bucketStartTimeNs + 1);
LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2);
+ FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -88,9 +89,10 @@
LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
+ FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index baaac67..0ba1c2f 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/metrics/EventMetricProducer.h"
+#include "src/dimension.h"
#include "metrics_test_helper.h"
#include <gmock/gmock.h>
@@ -88,25 +89,28 @@
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+ int tagId = 1;
+ int conditionTagId = 2;
+
EventMetric metric;
metric.set_name("1");
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
MetricConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
- link->add_key_in_what()->set_key(1);
- link->add_key_in_condition()->set_key(2);
+ *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+ *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
- LogEvent event1(1, bucketStartTimeNs + 1);
- event1.write("111"); // uid
+ LogEvent event1(tagId, bucketStartTimeNs + 1);
+ EXPECT_TRUE(event1.write("111"));
event1.init();
ConditionKey key1;
- key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
+ key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
- LogEvent event2(1, bucketStartTimeNs + 10);
- event2.write("222"); // uid
+ LogEvent event2(tagId, bucketStartTimeNs + 10);
+ EXPECT_TRUE(event2.write("222"));
event2.init();
ConditionKey key2;
- key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
+ key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 584a6d3..359851f 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,11 @@
GaugeMetric metric;
metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.mutable_gauge_fields()->add_field_num(2);
+ metric.mutable_gauge_fields_filter()->set_include_all(false);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(1);
+ gaugeFieldMatcher->add_child()->set_field(3);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -64,30 +68,41 @@
vector<shared_ptr<LogEvent>> allData;
allData.clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
+ event->write(10);
+ event->write("some value");
event->write(11);
event->init();
allData.push_back(event);
gaugeProducer.onDataPulled(allData);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(11, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+ EXPECT_EQ(10, it->second.value_int());
+ it++;
+ EXPECT_EQ(11, it->second.value_int());
EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
allData.clear();
std::shared_ptr<LogEvent> event2 =
std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
- event2->write(tagId);
+ event2->write(24);
+ event2->write("some value");
event2->write(25);
event2->init();
allData.push_back(event2);
gaugeProducer.onDataPulled(allData);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(25, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+ EXPECT_EQ(24, it->second.value_int());
+ it++;
+ EXPECT_EQ(25, it->second.value_int());
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(11L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+ EXPECT_EQ(10L, it->second.value_int());
+ it++;
+ EXPECT_EQ(11L, it->second.value_int());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
@@ -95,7 +110,10 @@
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+ EXPECT_EQ(24L, it->second.value_int());
+ it++;
+ EXPECT_EQ(25L, it->second.value_int());
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
}
@@ -103,7 +121,9 @@
GaugeMetric metric;
metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.mutable_gauge_fields()->add_field_num(2);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(2);
metric.set_condition("SCREEN_ON");
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -116,7 +136,7 @@
.WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(tagId);
+ event->write("some value");
event->write(100);
event->init();
data->push_back(event);
@@ -128,28 +148,32 @@
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(100,
+ gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(1);
+ event->write("some value");
event->write(110);
event->init();
allData.push_back(event);
gaugeProducer.onDataPulled(allData);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(110,
+ gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back()
+ .mGaugeFields->begin()->second.value_int());
gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+ EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back()
+ .mGaugeFields->begin()->second.value_int());
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
}
@@ -164,7 +188,9 @@
GaugeMetric metric;
metric.set_name(metricName);
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
- metric.mutable_gauge_fields()->add_field_num(2);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(2);
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, tagId, bucketStartTimeNs, pullerManager);
@@ -175,46 +201,50 @@
alert.set_number_of_buckets(2);
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
- std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
- event1->write(1);
+ int tagId = 1;
+ std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ event1->write("some value");
event1->write(13);
event1->init();
gaugeProducer.onDataPulled({event1});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(13L,
+ gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL);
std::shared_ptr<LogEvent> event2 =
- std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10);
- event2->write(1);
+ std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 10);
+ event2->write("some value");
event2->write(15);
event2->init();
gaugeProducer.onDataPulled({event2});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(15L,
+ gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event2->GetTimestampNs());
std::shared_ptr<LogEvent> event3 =
- std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
- event3->write(1);
+ std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+ event3->write("some value");
event3->write(24);
event3->init();
gaugeProducer.onDataPulled({event3});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_EQ(24L,
+ gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
// The event4 does not have the gauge field. Thus the current bucket value is 0.
std::shared_ptr<LogEvent> event4 =
- std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10);
- event4->write(1);
+ std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10);
+ event4->write("some value");
event4->init();
gaugeProducer.onDataPulled({event4});
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(0, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+ EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second->empty());
EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
}
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 4ad1db1..7843ca0 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -38,10 +38,12 @@
const ConfigKey kConfigKey(0, "test");
-const HashableDimensionKey eventKey = getMockedDimensionKey(0, "1");
-const HashableDimensionKey conditionKey = getMockedDimensionKey(4, "1");
-const HashableDimensionKey key1 = getMockedDimensionKey(1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(1, "2");
+const int TagId = 1;
+
+const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
+const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -174,7 +176,7 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
- HashableDimensionKey eventKey = getMockedDimensionKey(2, "maps");
+ HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
conditionKey1["APP_BACKGROUND"] = conditionKey;
EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index e0f554d..550b059 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -35,11 +35,12 @@
namespace statsd {
const ConfigKey kConfigKey(0, "test");
-const HashableDimensionKey eventKey = getMockedDimensionKey(0, "event");
+const int TagId = 1;
+const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(1, "maps");
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(3, "maps");
+const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
TEST(OringDurationTrackerTest, TestDurationOverlap) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index a0a854a..fc7245c 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -18,15 +18,12 @@
namespace os {
namespace statsd {
-HashableDimensionKey getMockedDimensionKey(int key, string value) {
- KeyValuePair pair;
- pair.set_key(key);
- pair.set_value_str(value);
-
- vector<KeyValuePair> pairs;
- pairs.push_back(pair);
-
- return HashableDimensionKey(pairs);
+HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) {
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(tagId);
+ dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+ return HashableDimensionKey(dimensionsValue);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 7cb3329..23e86f9 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -28,7 +28,7 @@
MOCK_METHOD2(
query,
ConditionState(const int conditionIndex,
- const std::map<std::string, HashableDimensionKey>& conditionParameters));
+ const ConditionKey& conditionParameters));
};
class MockStatsPullerManager : public StatsPullerManager {
@@ -38,7 +38,7 @@
MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
};
-HashableDimensionKey getMockedDimensionKey(int key, std::string value);
+HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
new file mode 100644
index 0000000..39e366f
--- /dev/null
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -0,0 +1,321 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+#include "statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
+ WakelockStateChanged::State state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_name(name);
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(4); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
+
+AtomMatcher CreateAcquireWakelockAtomMatcher() {
+ return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
+}
+
+AtomMatcher CreateReleaseWakelockAtomMatcher() {
+ return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
+}
+
+AtomMatcher CreateScreenStateChangedAtomMatcher(
+ const string& name, ScreenStateChanged::State state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_name(name);
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(1); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
+
+AtomMatcher CreateScreenTurnedOnAtomMatcher() {
+ return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", ScreenStateChanged::STATE_ON);
+}
+
+AtomMatcher CreateScreenTurnedOffAtomMatcher() {
+ return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", ScreenStateChanged::STATE_OFF);
+}
+
+AtomMatcher CreateSyncStateChangedAtomMatcher(
+ const string& name, SyncStateChanged::State state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_name(name);
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(3); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
+
+AtomMatcher CreateSyncStartAtomMatcher() {
+ return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
+}
+
+AtomMatcher CreateSyncEndAtomMatcher() {
+ return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
+}
+
+AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
+ const string& name, ActivityForegroundStateChanged::Activity activity) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_name(name);
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(4); // Activity field.
+ field_value_matcher->set_eq_int(activity);
+ return atom_matcher;
+}
+
+AtomMatcher CreateMoveToBackgroundAtomMatcher() {
+ return CreateActivityForegroundStateChangedAtomMatcher(
+ "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+}
+
+AtomMatcher CreateMoveToForegroundAtomMatcher() {
+ return CreateActivityForegroundStateChangedAtomMatcher(
+ "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+}
+
+AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
+ const string& name, ProcessLifeCycleStateChanged::Event event) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_name(name);
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(3); // Process state field.
+ field_value_matcher->set_eq_int(event);
+ return atom_matcher;
+}
+
+AtomMatcher CreateProcessCrashAtomMatcher() {
+ return CreateProcessLifeCycleStateChangedAtomMatcher(
+ "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
+}
+
+
+Predicate CreateScreenIsOnPredicate() {
+ Predicate predicate;
+ predicate.set_name("ScreenIsOn");
+ predicate.mutable_simple_predicate()->set_start("ScreenTurnedOn");
+ predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOff");
+ return predicate;
+}
+
+Predicate CreateScreenIsOffPredicate() {
+ Predicate predicate;
+ predicate.set_name("ScreenIsOff");
+ predicate.mutable_simple_predicate()->set_start("ScreenTurnedOff");
+ predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOn");
+ return predicate;
+}
+
+Predicate CreateHoldingWakelockPredicate() {
+ Predicate predicate;
+ predicate.set_name("HoldingWakelock");
+ predicate.mutable_simple_predicate()->set_start("AcquireWakelock");
+ predicate.mutable_simple_predicate()->set_stop("ReleaseWakelock");
+ return predicate;
+}
+
+Predicate CreateIsSyncingPredicate() {
+ Predicate predicate;
+ predicate.set_name("IsSyncing");
+ predicate.mutable_simple_predicate()->set_start("SyncStart");
+ predicate.mutable_simple_predicate()->set_stop("SyncEnd");
+ return predicate;
+}
+
+Predicate CreateIsInBackgroundPredicate() {
+ Predicate predicate;
+ predicate.set_name("IsInBackground");
+ predicate.mutable_simple_predicate()->set_start("MoveToBackground");
+ predicate.mutable_simple_predicate()->set_stop("MoveToForeground");
+ return predicate;
+}
+
+void addPredicateToPredicateCombination(const Predicate& predicate,
+ Predicate* combinationPredicate) {
+ combinationPredicate->mutable_combination()->add_predicate(predicate.name());
+}
+
+FieldMatcher CreateAttributionUidDimensions(const int atomId,
+ const std::vector<Position>& positions) {
+ FieldMatcher dimensions;
+ dimensions.set_field(atomId);
+ for (const auto position : positions) {
+ auto child = dimensions.add_child();
+ child->set_field(1);
+ child->set_position(position);
+ child->add_child()->set_field(1);
+ }
+ return dimensions;
+}
+
+FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
+ const std::vector<Position>& positions) {
+ FieldMatcher dimensions;
+ dimensions.set_field(atomId);
+ for (const auto position : positions) {
+ auto child = dimensions.add_child();
+ child->set_field(1);
+ child->set_position(position);
+ child->add_child()->set_field(1);
+ child->add_child()->set_field(2);
+ }
+ return dimensions;
+}
+
+FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
+ FieldMatcher dimensions;
+ dimensions.set_field(atomId);
+ for (const int field : fields) {
+ dimensions.add_child()->set_field(field);
+ }
+ return dimensions;
+}
+
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+ const ScreenStateChanged::State state, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(state));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
+ const std::vector<AttributionNode>& attributions, const string& wakelockName,
+ const WakelockStateChanged::State state, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
+ event->write(attributions);
+ event->write(WakelockStateChanged::PARTIAL);
+ event->write(wakelockName);
+ event->write(state);
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
+ const std::vector<AttributionNode>& attributions,
+ const string& wakelockName, uint64_t timestampNs) {
+ return CreateWakelockStateChangedEvent(
+ attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
+ const std::vector<AttributionNode>& attributions,
+ const string& wakelockName, uint64_t timestampNs) {
+ return CreateWakelockStateChangedEvent(
+ attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+ const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
+ event->write(uid);
+ event->write("pkg_name");
+ event->write("class_name");
+ event->write(activity);
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
+ return CreateActivityForegroundStateChangedEvent(
+ uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
+ return CreateActivityForegroundStateChangedEvent(
+ uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
+ const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
+ event->write(uid);
+ event->write(name);
+ event->write(state);
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateSyncStartEvent(
+ const int uid, const string& name, uint64_t timestampNs){
+ return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateSyncEndEvent(
+ const int uid, const string& name, uint64_t timestampNs) {
+ return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
+ const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) {
+ auto logEvent = std::make_unique<LogEvent>(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
+ logEvent->write(uid);
+ logEvent->write("");
+ logEvent->write(event);
+ logEvent->init();
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
+ return CreateProcessLifeCycleStateChangedEvent(
+ uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs);
+}
+
+sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
+ const ConfigKey& key) {
+ sp<UidMap> uidMap = new UidMap();
+ sp<AnomalyMonitor> anomalyMonitor = new AnomalyMonitor(10); // 10 seconds
+ sp<StatsLogProcessor> processor = new StatsLogProcessor(
+ uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){});
+ processor->OnConfigUpdated(key, config);
+ return processor;
+}
+
+AttributionNode CreateAttribution(const int& uid, const string& tag) {
+ AttributionNode attribution;
+ attribution.set_uid(uid);
+ attribution.set_tag(tag);
+ return attribution;
+}
+
+void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
+ std::sort(events->begin(), events->end(),
+ [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
+ return a->GetTimestampNs() < b->GetTimestampNs();
+ });
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
new file mode 100644
index 0000000..282e1b2
--- /dev/null
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -0,0 +1,126 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "statslog.h"
+#include "src/logd/LogEvent.h"
+#include "src/StatsLogProcessor.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Create AtomMatcher proto for acquiring wakelock.
+AtomMatcher CreateAcquireWakelockAtomMatcher();
+
+// Create AtomMatcher proto for releasing wakelock.
+AtomMatcher CreateReleaseWakelockAtomMatcher() ;
+
+// Create AtomMatcher proto for screen turned on.
+AtomMatcher CreateScreenTurnedOnAtomMatcher();
+
+// Create AtomMatcher proto for screen turned off.
+AtomMatcher CreateScreenTurnedOffAtomMatcher();
+
+// Create AtomMatcher proto for app sync turned on.
+AtomMatcher CreateSyncStartAtomMatcher();
+
+// Create AtomMatcher proto for app sync turned off.
+AtomMatcher CreateSyncEndAtomMatcher();
+
+// Create AtomMatcher proto for app sync moves to background.
+AtomMatcher CreateMoveToBackgroundAtomMatcher();
+
+// Create AtomMatcher proto for app sync moves to foreground.
+AtomMatcher CreateMoveToForegroundAtomMatcher();
+
+// Create AtomMatcher proto for process crashes
+AtomMatcher CreateProcessCrashAtomMatcher() ;
+
+// Create Predicate proto for screen is on.
+Predicate CreateScreenIsOnPredicate();
+
+// Create Predicate proto for screen is off.
+Predicate CreateScreenIsOffPredicate();
+
+// Create Predicate proto for holding wakelock.
+Predicate CreateHoldingWakelockPredicate();
+
+// Create a Predicate proto for app syncing.
+Predicate CreateIsSyncingPredicate();
+
+// Create a Predicate proto for app is in background.
+Predicate CreateIsInBackgroundPredicate();
+
+// Add a predicate to the predicate combination.
+void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
+
+// Create dimensions from primitive fields.
+FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
+
+// Create dimensions by attribution uid and tag.
+FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
+ const std::vector<Position>& positions);
+
+// Create dimensions by attribution uid only.
+FieldMatcher CreateAttributionUidDimensions(const int atomId,
+ const std::vector<Position>& positions);
+
+// Create log event for screen state changed.
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+ const ScreenStateChanged::State state, uint64_t timestampNs);
+
+// Create log event for app moving to background.
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
+
+// Create log event for app moving to foreground.
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+
+// Create log event when the app sync starts.
+std::unique_ptr<LogEvent> CreateSyncStartEvent(
+ const int uid, const string& name, uint64_t timestampNs);
+
+// Create log event when the app sync ends.
+std::unique_ptr<LogEvent> CreateSyncEndEvent(
+ const int uid, const string& name, uint64_t timestampNs);
+
+// Create log event when the app sync ends.
+std::unique_ptr<LogEvent> CreateAppCrashEvent(
+ const int uid, uint64_t timestampNs);
+
+// Create log event for acquiring wakelock.
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
+ const std::vector<AttributionNode>& attributions,
+ const string& wakelockName, uint64_t timestampNs);
+
+// Create log event for releasing wakelock.
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
+ const std::vector<AttributionNode>& attributions,
+ const string& wakelockName, uint64_t timestampNs);
+
+// Helper function to create an AttributionNode proto.
+AttributionNode CreateAttribution(const int& uid, const string& tag);
+
+// Create a statsd log event processor upon the start time in seconds, config and key.
+sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
+ const ConfigKey& key);
+
+// Util function to sort the log events by timestamp.
+void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index fe3d86d..9294681 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -71,20 +71,25 @@
return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
}
- private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) {
- for (com.android.os.StatsLog.KeyValuePair kv : pairs) {
- sb.append(kv.getKey()).append(":");
- if (kv.hasValueBool()) {
- sb.append(kv.getValueBool());
- } else if (kv.hasValueFloat()) {
- sb.append(kv.getValueFloat());
- } else if (kv.hasValueInt()) {
- sb.append(kv.getValueInt());
- } else if (kv.hasValueStr()) {
- sb.append(kv.getValueStr());
+ private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
+ sb.append(dimensionValue.getField()).append(":");
+ if (dimensionValue.hasValueBool()) {
+ sb.append(dimensionValue.getValueBool());
+ } else if (dimensionValue.hasValueFloat()) {
+ sb.append(dimensionValue.getValueFloat());
+ } else if (dimensionValue.hasValueInt()) {
+ sb.append(dimensionValue.getValueInt());
+ } else if (dimensionValue.hasValueStr()) {
+ sb.append(dimensionValue.getValueStr());
+ } else if (dimensionValue.hasValueTuple()) {
+ sb.append("{");
+ for (StatsLog.DimensionsValue child :
+ dimensionValue.getValueTuple().getDimensionsValueList()) {
+ displayDimension(sb, child);
}
- sb.append(" ");
+ sb.append("}");
}
+ sb.append(" ");
}
public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
@@ -93,7 +98,7 @@
sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
sb.append("dimension: ");
- displayDimension(sb, duration.getDimensionList());
+ displayDimension(sb, duration.getDimension());
sb.append("\n");
for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
@@ -120,7 +125,7 @@
sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
sb.append("dimension: ");
- displayDimension(sb, count.getDimensionList());
+ displayDimension(sb, count.getDimension());
sb.append("\n");
for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 70dd634..137fd4d 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -252,7 +252,9 @@
Log.d(TAG, "invalid pkg id");
return;
}
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, mUids[id], 0, name, 1);
+ int[] uids = new int[] {mUids[id]};
+ String[] tags = new String[] {"acquire"};
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 1);");
@@ -264,7 +266,9 @@
Log.d(TAG, "invalid pkg id");
return;
}
- StatsLog.write(10, mUids[id], 0, name, 0);
+ int[] uids = new int[] {mUids[id]};
+ String[] tags = new String[] {"release"};
+ StatsLog.write(10, uids, tags, 0, name, 0);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 0);");
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index 5fc4cf5..f516477 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -27,8 +27,7 @@
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.internal.os.StatsdConfigProto.KeyMatcher;
-import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimplePredicate;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
index 735a327..ba43d57 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -71,20 +71,25 @@
return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
}
- private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) {
- for (com.android.os.StatsLog.KeyValuePair kv : pairs) {
- sb.append(kv.getKey()).append(":");
- if (kv.hasValueBool()) {
- sb.append(kv.getValueBool());
- } else if (kv.hasValueFloat()) {
- sb.append(kv.getValueFloat());
- } else if (kv.hasValueInt()) {
- sb.append(kv.getValueInt());
- } else if (kv.hasValueStr()) {
- sb.append(kv.getValueStr());
+ private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
+ sb.append(dimensionValue.getField()).append(":");
+ if (dimensionValue.hasValueBool()) {
+ sb.append(dimensionValue.getValueBool());
+ } else if (dimensionValue.hasValueFloat()) {
+ sb.append(dimensionValue.getValueFloat());
+ } else if (dimensionValue.hasValueInt()) {
+ sb.append(dimensionValue.getValueInt());
+ } else if (dimensionValue.hasValueStr()) {
+ sb.append(dimensionValue.getValueStr());
+ } else if (dimensionValue.hasValueTuple()) {
+ sb.append("{");
+ for (StatsLog.DimensionsValue child :
+ dimensionValue.getValueTuple().getDimensionsValueList()) {
+ displayDimension(sb, child);
}
- sb.append(" ");
+ sb.append("}");
}
+ sb.append(" ");
}
public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
@@ -93,7 +98,7 @@
sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
sb.append("dimension: ");
- displayDimension(sb, duration.getDimensionList());
+ displayDimension(sb, duration.getDimension());
sb.append("\n");
for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
@@ -120,7 +125,7 @@
sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
sb.append("dimension: ");
- displayDimension(sb, count.getDimensionList());
+ displayDimension(sb, count.getDimension());
sb.append("\n");
for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8966585..72f07b7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4118,13 +4118,10 @@
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
- // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid.
- // The debug logging here can be deleted once statsd is wired up.
- if (DEBUG) {
- Slog.w(TAG, "StatsLog [start]: uid=" + uid + ", type=" + type + ", name=" + name
- + ", wc=" + wc);
+ if (wc != null) {
+ StatsLog.write(
+ StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
}
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 1);
}
}
@@ -4161,14 +4158,10 @@
}
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
-
- // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid.
- // The debug logging here can be deleted once statsd is wired up.
- if (DEBUG) {
- Slog.w(TAG, "StatsLog [stop]: uid=" + uid + ", type=" + type + ", name=" + name
- + ", wc=" + wc);
+ if (wc != null) {
+ StatsLog.write(
+ StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
}
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 0);
}
}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index a25784d..80853b1 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -112,7 +112,7 @@
case FieldDescriptor::TYPE_MESSAGE:
// TODO: not the final package name
if (field->message_type()->full_name() ==
- "android.os.statsd.AttributionChain") {
+ "android.os.statsd.AttributionNode") {
return JAVA_TYPE_ATTRIBUTION_CHAIN;
} else {
return JAVA_TYPE_OBJECT;
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 5e93c08..89749fb 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -769,7 +769,7 @@
AtomDecl attributionDecl;
vector<java_type_t> attributionSignature;
- collate_atom(android::os::statsd::Attribution::descriptor(),
+ collate_atom(android::os::statsd::AttributionNode::descriptor(),
&attributionDecl, &attributionSignature);
// Write the .cpp file
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 84b22cda..66cbee8 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -39,7 +39,7 @@
}
message AllTypesAtom {
- optional android.os.statsd.AttributionChain attribution_chain = 1;
+ repeated android.os.statsd.AttributionNode attribution_chain = 1;
optional double double_field = 2;
optional float float_field = 3;
optional int64 int64_field = 4;
@@ -99,12 +99,12 @@
}
}
-message BadAttributionChainPositionAtom {
+message BadAttributionNodePositionAtom {
optional int32 field1 = 1;
- optional android.os.statsd.AttributionChain attribution = 2;
+ repeated android.os.statsd.AttributionNode attribution = 2;
}
-message BadAttributionChainPosition {
- oneof event { BadAttributionChainPositionAtom bad = 1; }
+message BadAttributionNodePosition {
+ oneof event { BadAttributionNodePositionAtom bad = 1; }
}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index b2b7298..9e22cd9 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -191,10 +191,10 @@
* Test that atoms that have an attribution chain not in the first position are
* rejected.
*/
-TEST(CollationTest, FailBadAttributionChainPosition) {
+TEST(CollationTest, FailBadAttributionNodePosition) {
Atoms atoms;
int errorCount =
- collate_atoms(BadAttributionChainPosition::descriptor(), &atoms);
+ collate_atoms(BadAttributionNodePosition::descriptor(), &atoms);
EXPECT_EQ(1, errorCount);
}