1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define DEBUG false // STOPSHIP if true
18 #include "Log.h"
19
20 #include "StatsPullerManagerImpl.h"
21 #include "puller_util.h"
22 #include "statslog.h"
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
28 using std::map;
29 using std::shared_ptr;
30 using std::vector;
31
32 namespace {
shouldMerge(shared_ptr<LogEvent> & lhs,shared_ptr<LogEvent> & rhs,const vector<int> & nonAdditiveFields)33 bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
34 const vector<int>& nonAdditiveFields) {
35 const auto& l_values = lhs->getValues();
36 const auto& r_values = rhs->getValues();
37
38 for (size_t i : nonAdditiveFields) {
39 // We store everything starting from index 0, so we need to use i-1
40 if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
41 l_values[i - 1].mValue == r_values[i - 1].mValue)) {
42 return false;
43 }
44 }
45 return true;
46 }
47
48 // merge rhs to lhs
49 // when calling this function, all sanity check should be done already.
50 // e.g., index boundary, nonAdditiveFields matching etc.
mergeEvent(shared_ptr<LogEvent> & lhs,shared_ptr<LogEvent> & rhs,const vector<int> & additiveFields)51 bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
52 const vector<int>& additiveFields) {
53 vector<FieldValue>* host_values = lhs->getMutableValues();
54 const auto& child_values = rhs->getValues();
55 for (int i : additiveFields) {
56 Value& host = (*host_values)[i - 1].mValue;
57 const Value& child = (child_values[i - 1]).mValue;
58 if (child.getType() != host.getType()) {
59 return false;
60 }
61 switch (child.getType()) {
62 case INT:
63 host.setInt(host.int_value + child.int_value);
64 break;
65 case LONG:
66 host.setLong(host.long_value + child.long_value);
67 break;
68 default:
69 ALOGE("Tried to merge 2 fields with unsupported type");
70 return false;
71 }
72 }
73 return true;
74 }
75
tryMerge(vector<shared_ptr<LogEvent>> & data,int child_pos,const vector<int> & host_pos,const vector<int> & nonAdditiveFields,const vector<int> & additiveFields)76 bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
77 const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
78 for (const auto& pos : host_pos) {
79 if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
80 mergeEvent(data[pos], data[child_pos], additiveFields)) {
81 return true;
82 }
83 }
84 return false;
85 }
86
87 } // namespace
88
89 /**
90 * Process all data and merge isolated with host if necessary.
91 * For example:
92 * NetworkBytesAtom {
93 * int uid = 1;
94 * State process_state = 2;
95 * int byte_send = 3;
96 * int byte_recv = 4;
97 * }
98 * additive fields are {3, 4}, non-additive field is {2}
99 * If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
100 * [uid1, fg, 100, 200]
101 * [uid1_child, fg, 100, 200]
102 * [uid1, bg, 100, 200]
103 *
104 * We want to merge them and results should be:
105 * [uid1, fg, 200, 400]
106 * [uid1, bg, 100, 200]
107 */
mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>> & data,const sp<UidMap> & uidMap,int tagId)108 void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
109 int tagId) {
110 if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
111 StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
112 VLOG("Unknown pull atom id %d", tagId);
113 return;
114 }
115 int uidField;
116 auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
117 if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
118 VLOG("No uid to merge for atom %d", tagId);
119 return;
120 } else {
121 uidField = it->second; // uidField is the field number in proto,
122 }
123 const vector<int>& additiveFields =
124 StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.additiveFields;
125 const vector<int>& nonAdditiveFields =
126 StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
127
128 // map of host uid to their position in the original vector
129 map<int, vector<int>> hostPosition;
130 vector<bool> toRemove = vector<bool>(data.size(), false);
131
132 for (size_t i = 0; i < data.size(); i++) {
133 vector<FieldValue>* valueList = data[i]->getMutableValues();
134
135 int uid;
136 if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
137 (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
138 uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
139 } else {
140 ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
141 continue;
142 }
143
144 const int hostUid = uidMap->getHostUidOrSelf(uid);
145
146 if (hostUid != uid) {
147 (*valueList)[0].mValue.setInt(hostUid);
148 }
149 if (hostPosition.find(hostUid) == hostPosition.end()) {
150 hostPosition[hostUid].push_back(i);
151 } else {
152 if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
153 toRemove[i] = true;
154 } else {
155 hostPosition[hostUid].push_back(i);
156 }
157 }
158 }
159
160 vector<shared_ptr<LogEvent>> mergedData;
161 for (size_t i = 0; i < toRemove.size(); i++) {
162 if (!toRemove[i]) {
163 mergedData.push_back(data[i]);
164 }
165 }
166 data.clear();
167 data = mergedData;
168 }
169
170 } // namespace statsd
171 } // namespace os
172 } // namespace android
173