Map isolated uid to host uid when processing log event in statsD.

Test: added test case for isolated uid in Attribution e2e test.
Change-Id: I63d16ebee3e611b1ef0c910e5154cf27766cb330
This commit is contained in:
Yangster-mac
2018-01-09 16:29:22 -08:00
parent d6808dc0c0
commit d40053eb8b
21 changed files with 166 additions and 70 deletions

View File

@@ -25,6 +25,8 @@
#include "guardrail/StatsdStats.h"
#include "metrics/CountMetricProducer.h"
#include "external/StatsPullerManager.h"
#include "dimension.h"
#include "field_util.h"
#include "stats_util.h"
#include "storage/StorageManager.h"
@@ -88,30 +90,56 @@ void StatsLogProcessor::onAnomalyAlarmFired(
}
}
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
// pass the event to metrics managers.
for (auto& pair : mMetricsManagers) {
pair.second->onLogEvent(msg);
flushIfNecessaryLocked(msg.GetTimestampNs(), pair.first, *(pair.second));
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
std::vector<Field> uidFields;
findFields(
event->getFieldValueMap(),
buildAttributionUidFieldMatcher(event->GetTagId(), Position::ANY),
&uidFields);
for (size_t i = 0; i < uidFields.size(); ++i) {
DimensionsValue* value = event->findFieldValueOrNull(uidFields[i]);
if (value != nullptr && value->value_case() == DimensionsValue::ValueCase::kValueInt) {
const int uid = mUidMap->getHostUidOrSelf(value->value_int());
value->set_value_int(uid);
}
}
}
void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) {
status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
bool is_create = event.GetBool(3, &err);
auto parent_uid = int(event.GetLong(1, &err2));
auto isolated_uid = int(event.GetLong(2, &err3));
if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
if (is_create) {
mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
} else {
mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
}
} else {
ALOGE("Failed to parse uid in the isolated uid change event.");
}
}
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
StatsdStats::getInstance().noteAtomLogged(
event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC);
// 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) {
status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
bool is_create = msg.GetBool(3, &err);
auto parent_uid = int(msg.GetLong(1, &err2));
auto isolated_uid = int(msg.GetLong(2, &err3));
if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
if (is_create) {
mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
} else {
mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
}
}
if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
onIsolatedUidChangedEventLocked(*event);
} else {
// Map the isolated uid to host uid if necessary.
mapIsolatedUidToHostUidIfNecessaryLocked(event);
}
// pass the event to metrics managers.
for (auto& pair : mMetricsManagers) {
pair.second->onLogEvent(*event);
flushIfNecessaryLocked(event->GetTimestampNs(), pair.first, *(pair.second));
}
}

View File

@@ -38,7 +38,7 @@ public:
const std::function<void(const ConfigKey&)>& sendBroadcast);
virtual ~StatsLogProcessor();
void OnLogEvent(const LogEvent& event);
void OnLogEvent(LogEvent* event);
void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
@@ -46,7 +46,8 @@ public:
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);
void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs,
ConfigMetricsReportList* report);
/* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
void onAnomalyAlarmFired(
@@ -79,6 +80,12 @@ private:
void flushIfNecessaryLocked(uint64_t timestampNs, const ConfigKey& key,
MetricsManager& metricsManager);
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
// Function used to send a broadcast so that receiver for the config key can call getData
// to retrieve the stored data.
std::function<void(const ConfigKey& key)> mSendBroadcast;

View File

@@ -695,7 +695,7 @@ void StatsService::Startup() {
mConfigManager->Startup();
}
void StatsService::OnLogEvent(const LogEvent& event) {
void StatsService::OnLogEvent(LogEvent* event) {
mProcessor->OnLogEvent(event);
}

View File

@@ -72,7 +72,7 @@ public:
/**
* Called by LogReader when there's a log event to process.
*/
virtual void OnLogEvent(const LogEvent& event);
virtual void OnLogEvent(LogEvent* event);
/**
* Binder call for clients to request data for this configuration key.

View File

@@ -19,7 +19,6 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_internal.pb.h"
#include "dimension.h"
#include "field_util.h"
namespace android {
@@ -220,33 +219,35 @@ 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();
FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position) {
FieldMatcher matcher;
matcher.set_field(tagId);
auto 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);
child->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
return matcher;
}
void buildAttributionFieldMatcher(const int tagId, const Position position,
FieldMatcher *matcher) {
matcher->set_field(tagId);
FieldMatcher* child = matcher->add_child();
FieldMatcher 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(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
return matcher;
}
FieldMatcher 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->add_child()->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
child->add_child()->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
return matcher;
}
void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {

View File

@@ -27,7 +27,6 @@ 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);

View File

@@ -184,6 +184,7 @@ void setPositionForLeaf(Field *field, int index) {
}
}
namespace {
void findFields(
const FieldValueMap& fieldValueMap,
const FieldMatcher& matcher,
@@ -287,9 +288,18 @@ void findFields(
}
}
} // namespace
void findFields(
const FieldValueMap& fieldValueMap,
const FieldMatcher& matcher,
std::vector<Field>* rootFields) {
return findFields(fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), rootFields);
}
void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) {
std::vector<Field> rootFields;
findFields(*fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), &rootFields);
findFields(*fieldValueMap, matcher, &rootFields);
std::set<Field, FieldCmp> rootFieldSet(rootFields.begin(), rootFields.end());
auto it = fieldValueMap->begin();
while (it != fieldValueMap->end()) {
@@ -313,6 +323,11 @@ bool hasLeafNode(const FieldMatcher& matcher) {
return true;
}
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;
}
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -85,6 +85,9 @@ void findFields(
// Filter out the fields not in the field matcher.
void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap);
// Returns if the field is attribution node uid field.
bool IsAttributionUidField(const Field& field);
} // namespace statsd
} // namespace os
} // namespace android

View File

@@ -397,6 +397,14 @@ DimensionsValue LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField) c
return dimensionsValue;
}
DimensionsValue* LogEvent::findFieldValueOrNull(const Field& field) {
auto it = mFieldValueMap.find(field);
if (it == mFieldValueMap.end()) {
return nullptr;
}
return &it->second;
}
string LogEvent::ToString() const {
ostringstream result;
result << "{ " << mTimestampNs << " (" << mTagId << ")";

View File

@@ -132,6 +132,11 @@ public:
return mFieldValueMap.size();
}
/**
* Returns the mutable DimensionsValue proto for the specific the field.
*/
DimensionsValue* findFieldValueOrNull(const Field& field);
inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; }
private:

View File

@@ -33,7 +33,7 @@ public:
LogListener();
virtual ~LogListener();
virtual void OnLogEvent(const LogEvent& msg) = 0;
virtual void OnLogEvent(LogEvent* msg) = 0;
};
} // namespace statsd

View File

@@ -110,7 +110,7 @@ int LogReader::connect_and_read() {
LogEvent event(msg);
// Call the listener
mListener->OnLogEvent(event);
mListener->OnLogEvent(&event);
}
}

View File

@@ -93,11 +93,6 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera
return matched;
}
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;
}
bool matchesNonRepeatedField(
const UidMap& uidMap,
const FieldValueMap& fieldMap,

View File

@@ -269,7 +269,7 @@ void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) {
}
}
int UidMap::getParentUidOrSelf(int uid) {
int UidMap::getHostUidOrSelf(int uid) const {
lock_guard<mutex> lock(mIsolatedMutex);
auto it = mIsolatedUidMap.find(uid);

View File

@@ -90,8 +90,8 @@ public:
void assignIsolatedUid(int isolatedUid, int parentUid);
void removeIsolatedUid(int isolatedUid, int parentUid);
// Returns the parent uid if it exists. Otherwise, returns the same uid that was passed-in.
int getParentUidOrSelf(int uid);
// Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
int getHostUidOrSelf(int uid) const;
// Gets the output. If every config key has received the output, then the output is cleared.
UidMapping getOutput(const ConfigKey& key);

View File

@@ -45,18 +45,18 @@ TEST(UidMapTest, TestIsolatedUID) {
addEvent.write(1); // Indicates creation.
addEvent.init();
EXPECT_EQ(101, m->getParentUidOrSelf(101));
EXPECT_EQ(101, m->getHostUidOrSelf(101));
p.OnLogEvent(addEvent);
EXPECT_EQ(100, m->getParentUidOrSelf(101));
p.OnLogEvent(&addEvent);
EXPECT_EQ(100, m->getHostUidOrSelf(101));
LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
removeEvent.write(100); // parent UID
removeEvent.write(101); // isolated UID
removeEvent.write(0); // Indicates removal.
removeEvent.init();
p.OnLogEvent(removeEvent);
EXPECT_EQ(101, m->getParentUidOrSelf(101));
p.OnLogEvent(&removeEvent);
EXPECT_EQ(101, m->getHostUidOrSelf(101));
}
TEST(UidMapTest, TestMatching) {

View File

@@ -106,6 +106,10 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) {
{CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
std::vector<AttributionNode> attributions8 = {CreateAttribution(111, "App1")};
// GMS core node with isolated uid.
const int isolatedUid = 666;
std::vector<AttributionNode> attributions9 =
{CreateAttribution(isolatedUid, "GMSCoreModule3")};
std::vector<std::unique_ptr<LogEvent>> events;
// Events 1~4 are in the 1st bucket.
@@ -124,22 +128,30 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) {
events.push_back(CreateAcquireWakelockEvent(
attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
events.push_back(CreateAcquireWakelockEvent(
attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 1));
attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
events.push_back(CreateAcquireWakelockEvent(
attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
events.push_back(CreateAcquireWakelockEvent(
attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
events.push_back(CreateAcquireWakelockEvent(
attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
events.push_back(CreateIsolatedUidChangedEvent(
isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
events.push_back(CreateIsolatedUidChangedEvent(
isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(*event);
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs + 1, &reports);
processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &reports);
EXPECT_EQ(reports.reports_size(), 1);
EXPECT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::CountMetricDataWrapper countMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
EXPECT_EQ(countMetrics.data_size(), 3);
EXPECT_EQ(countMetrics.data_size(), 4);
auto data = countMetrics.data(0);
ValidateAttributionUidAndTagDimension(
@@ -164,6 +176,14 @@ TEST(AttributionE2eTest, TestAttributionMatchAndSlice) {
EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
data = countMetrics.data(2);
ValidateAttributionUidAndTagDimension(
data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule3");
EXPECT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 4 * bucketSizeNs);
data = countMetrics.data(3);
ValidateAttributionUidAndTagDimension(
data.dimension(), android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
EXPECT_EQ(data.bucket_info_size(), 1);

View File

@@ -188,7 +188,7 @@ TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(*event);
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;
processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);

View File

@@ -107,7 +107,7 @@ TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(*event);
processor->OnLogEvent(event.get());
}
ConfigMetricsReportList reports;

View File

@@ -292,6 +292,17 @@ std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampN
uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs);
}
std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) {
auto logEvent = std::make_unique<LogEvent>(
android::util::ISOLATED_UID_CHANGED, timestampNs);
logEvent->write(hostUid);
logEvent->write(isolatedUid);
logEvent->write(is_create);
logEvent->init();
return logEvent;
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
const ConfigKey& key) {
sp<UidMap> uidMap = new UidMap();

View File

@@ -111,6 +111,10 @@ std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
const std::vector<AttributionNode>& attributions,
const string& wakelockName, uint64_t timestampNs);
// Create log event for releasing wakelock.
std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
// Helper function to create an AttributionNode proto.
AttributionNode CreateAttribution(const int& uid, const string& tag);