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