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 "StatsPullerManager.h" 21 #include "puller_util.h" 22 #include "statslog.h" 23 24 namespace android { 25 namespace os { 26 namespace statsd { 27 28 using std::list; 29 using std::map; 30 using std::set; 31 using std::shared_ptr; 32 using std::sort; 33 using std::vector; 34 35 /** 36 * Process all data and merge isolated with host if necessary. 37 * For example: 38 * NetworkBytesAtom { 39 * int uid = 1; 40 * State process_state = 2; 41 * int byte_send = 3; 42 * int byte_recv = 4; 43 * } 44 * additive fields are {3, 4} 45 * If we pulled the following events (uid1_child is an isolated uid which maps to uid1): 46 * [uid1, fg, 100, 200] 47 * [uid1_child, fg, 100, 200] 48 * [uid1, bg, 100, 200] 49 * 50 * We want to merge them and results should be: 51 * [uid1, fg, 200, 400] 52 * [uid1, bg, 100, 200] 53 * 54 * All atoms should be of the same tagId. All fields should be present. 55 */ 56 void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, 57 int tagId) { 58 if (StatsPullerManager::kAllPullAtomInfo.find(tagId) == 59 StatsPullerManager::kAllPullAtomInfo.end()) { 60 VLOG("Unknown pull atom id %d", tagId); 61 return; 62 } 63 if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) == 64 android::util::AtomsInfo::kAtomsWithAttributionChain.end()) && 65 (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) == 66 android::util::AtomsInfo::kAtomsWithUidField.end())) { 67 VLOG("No uid or attribution chain to merge, atom %d", tagId); 68 return; 69 } 70 71 // 1. Map all isolated uid in-place to host uid 72 for (shared_ptr<LogEvent>& event : data) { 73 if (event->GetTagId() != tagId) { 74 ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId()); 75 return; 76 } 77 if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) != 78 android::util::AtomsInfo::kAtomsWithAttributionChain.end()) { 79 for (auto& value : *(event->getMutableValues())) { 80 if (value.mField.getPosAtDepth(0) > kAttributionField) { 81 break; 82 } 83 if (isAttributionUidField(value)) { 84 const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value); 85 value.mValue.setInt(hostUid); 86 } 87 } 88 } else { 89 auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId); 90 if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { 91 int uidField = it->second; // uidField is the field number in proto, 92 // starting from 1 93 if (uidField > 0 && (int)event->getValues().size() >= uidField && 94 (event->getValues())[uidField - 1].mValue.getType() == INT) { 95 Value& value = (*event->getMutableValues())[uidField - 1].mValue; 96 const int hostUid = uidMap->getHostUidOrSelf(value.int_value); 97 value.setInt(hostUid); 98 } else { 99 ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); 100 return; 101 } 102 } 103 } 104 } 105 106 // 2. sort the data, bit-wise 107 sort(data.begin(), data.end(), 108 [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) { 109 if (lhs->size() != rhs->size()) { 110 return lhs->size() < rhs->size(); 111 } 112 const std::vector<FieldValue>& lhsValues = lhs->getValues(); 113 const std::vector<FieldValue>& rhsValues = rhs->getValues(); 114 for (int i = 0; i < (int)lhs->size(); i++) { 115 if (lhsValues[i] != rhsValues[i]) { 116 return lhsValues[i] < rhsValues[i]; 117 } 118 } 119 return false; 120 }); 121 122 vector<shared_ptr<LogEvent>> mergedData; 123 const vector<int>& additiveFieldsVec = 124 StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields; 125 const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end()); 126 bool needMerge = true; 127 128 // 3. do the merge. 129 // The loop invariant is this: for every event, check if it differs on 130 // non-additive fields, or have different attribution chain length. 131 // If so, no need to merge, add itself to the result. 132 // Otherwise, merge the value onto the one immediately next to it. 133 for (int i = 0; i < (int)data.size() - 1; i++) { 134 // Size different, must be different chains. 135 if (data[i]->size() != data[i + 1]->size()) { 136 mergedData.push_back(data[i]); 137 continue; 138 } 139 vector<FieldValue>* lhsValues = data[i]->getMutableValues(); 140 vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues(); 141 needMerge = true; 142 for (int p = 0; p < (int)lhsValues->size(); p++) { 143 if ((*lhsValues)[p] != (*rhsValues)[p]) { 144 int pos = (*lhsValues)[p].mField.getPosAtDepth(0); 145 // Differ on non-additive field, abort. 146 if (additiveFields.find(pos) == additiveFields.end()) { 147 needMerge = false; 148 break; 149 } 150 } 151 } 152 if (!needMerge) { 153 mergedData.push_back(data[i]); 154 continue; 155 } 156 // This should be infrequent operation. 157 for (int p = 0; p < (int)lhsValues->size(); p++) { 158 int pos = (*lhsValues)[p].mField.getPosAtDepth(0); 159 if (additiveFields.find(pos) != additiveFields.end()) { 160 (*rhsValues)[p].mValue += (*lhsValues)[p].mValue; 161 } 162 } 163 } 164 mergedData.push_back(data.back()); 165 166 data.clear(); 167 data = mergedData; 168 } 169 170 } // namespace statsd 171 } // namespace os 172 } // namespace android 173