/*
* Copyright (C) 2018 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.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsPullerManagerImpl.h"
#include "puller_util.h"
#include "statslog.h"
namespace android {
namespace os {
namespace statsd {
using std::map;
using std::shared_ptr;
using std::vector;
namespace {
bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
const vector<int>& nonAdditiveFields) {
const auto& l_values = lhs->getValues();
const auto& r_values = rhs->getValues();
for (size_t i : nonAdditiveFields) {
// We store everything starting from index 0, so we need to use i-1
if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
l_values[i - 1].mValue == r_values[i - 1].mValue)) {
return false;
}
}
return true;
}
// merge rhs to lhs
// when calling this function, all sanity check should be done already.
// e.g., index boundary, nonAdditiveFields matching etc.
bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
const vector<int>& additiveFields) {
vector<FieldValue>* host_values = lhs->getMutableValues();
const auto& child_values = rhs->getValues();
for (int i : additiveFields) {
Value& host = (*host_values)[i - 1].mValue;
const Value& child = (child_values[i - 1]).mValue;
if (child.getType() != host.getType()) {
return false;
}
switch (child.getType()) {
case INT:
host.setInt(host.int_value + child.int_value);
break;
case LONG:
host.setLong(host.long_value + child.long_value);
break;
default:
ALOGE("Tried to merge 2 fields with unsupported type");
return false;
}
}
return true;
}
bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
for (const auto& pos : host_pos) {
if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
mergeEvent(data[pos], data[child_pos], additiveFields)) {
return true;
}
}
return false;
}
} // namespace
/**
* Process all data and merge isolated with host if necessary.
* For example:
* NetworkBytesAtom {
* int uid = 1;
* State process_state = 2;
* int byte_send = 3;
* int byte_recv = 4;
* }
* additive fields are {3, 4}, non-additive field is {2}
* If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
* [uid1, fg, 100, 200]
* [uid1_child, fg, 100, 200]
* [uid1, bg, 100, 200]
*
* We want to merge them and results should be:
* [uid1, fg, 200, 400]
* [uid1, bg, 100, 200]
*/
void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
int tagId) {
if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
VLOG("Unknown pull atom id %d", tagId);
return;
}
int uidField;
auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
VLOG("No uid to merge for atom %d", tagId);
return;
} else {
uidField = it->second; // uidField is the field number in proto,
}
const vector<int>& additiveFields =
StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
const vector<int>& nonAdditiveFields =
StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
// map of host uid to their position in the original vector
map<int, vector<int>> hostPosition;
vector<bool> toRemove = vector<bool>(data.size(), false);
for (size_t i = 0; i < data.size(); i++) {
vector<FieldValue>* valueList = data[i]->getMutableValues();
int uid;
if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
(data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
} else {
ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
continue;
}
const int hostUid = uidMap->getHostUidOrSelf(uid);
if (hostUid != uid) {
(*valueList)[0].mValue.setInt(hostUid);
}
if (hostPosition.find(hostUid) == hostPosition.end()) {
hostPosition[hostUid].push_back(i);
} else {
if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
toRemove[i] = true;
} else {
hostPosition[hostUid].push_back(i);
}
}
}
vector<shared_ptr<LogEvent>> mergedData;
for (size_t i = 0; i < toRemove.size(); i++) {
if (!toRemove[i]) {
mergedData.push_back(data[i]);
}
}
data.clear();
data = mergedData;
}
} // namespace statsd
} // namespace os
} // namespace android