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 { 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. 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 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 */ 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