1 /* 2 * Copyright (C) 2017 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 #define DEBUG false // STOPSHIP if true 17 #include "Log.h" 18 19 #include "HashableDimensionKey.h" 20 #include "FieldValue.h" 21 22 namespace android { 23 namespace os { 24 namespace statsd { 25 26 using std::string; 27 using std::vector; 28 using android::base::StringPrintf; 29 30 /** 31 * Recursive helper function that populates a parent StatsDimensionsValueParcel 32 * with children StatsDimensionsValueParcels. 33 * 34 * \param parent parcel that will be populated with children 35 * \param childDepth depth of children FieldValues 36 * \param childPrefix expected FieldValue prefix of children 37 * \param dims vector of FieldValues stored by HashableDimensionKey 38 * \param index position in dims to start reading children from 39 */ 40 static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent, 41 int childDepth, int childPrefix, 42 const vector<FieldValue>& dims, 43 size_t& index) { 44 if (childDepth > 2) { 45 ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel."); 46 return; 47 } 48 49 while (index < dims.size()) { 50 const FieldValue& dim = dims[index]; 51 int fieldDepth = dim.mField.getDepth(); 52 int fieldPrefix = dim.mField.getPrefix(childDepth); 53 54 StatsDimensionsValueParcel child; 55 child.field = dim.mField.getPosAtDepth(childDepth); 56 57 if (fieldDepth == childDepth && fieldPrefix == childPrefix) { 58 switch (dim.mValue.getType()) { 59 case INT: 60 child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE; 61 child.intValue = dim.mValue.int_value; 62 break; 63 case LONG: 64 child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE; 65 child.longValue = dim.mValue.long_value; 66 break; 67 case FLOAT: 68 child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE; 69 child.floatValue = dim.mValue.float_value; 70 break; 71 case STRING: 72 child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE; 73 child.stringValue = dim.mValue.str_value; 74 break; 75 default: 76 ALOGE("Encountered FieldValue with unsupported value type."); 77 break; 78 } 79 index++; 80 parent.tupleValue.push_back(child); 81 } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) { 82 // This FieldValue is not a child of the current parent, but it is 83 // an indirect descendant. Thus, create a direct child of TUPLE_TYPE 84 // and recurse to parcel the indirect descendants. 85 child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; 86 populateStatsDimensionsValueParcelChildren(child, childDepth + 1, 87 dim.mField.getPrefix(childDepth + 1), dims, 88 index); 89 parent.tupleValue.push_back(child); 90 } else { 91 return; 92 } 93 } 94 } 95 96 StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const { 97 StatsDimensionsValueParcel root; 98 if (mValues.size() == 0) { 99 return root; 100 } 101 102 root.field = mValues[0].mField.getTag(); 103 root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE; 104 105 // Children of the root correspond to top-level (depth = 0) FieldValues. 106 int childDepth = 0; 107 int childPrefix = 0; 108 size_t index = 0; 109 populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index); 110 111 return root; 112 } 113 114 android::hash_t hashDimension(const HashableDimensionKey& value) { 115 android::hash_t hash = 0; 116 for (const auto& fieldValue : value.getValues()) { 117 hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getField())); 118 hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mField.getTag())); 119 hash = android::JenkinsHashMix(hash, android::hash_type((int)fieldValue.mValue.getType())); 120 switch (fieldValue.mValue.getType()) { 121 case INT: 122 hash = android::JenkinsHashMix(hash, 123 android::hash_type(fieldValue.mValue.int_value)); 124 break; 125 case LONG: 126 hash = android::JenkinsHashMix(hash, 127 android::hash_type(fieldValue.mValue.long_value)); 128 break; 129 case STRING: 130 hash = android::JenkinsHashMix(hash, static_cast<uint32_t>(std::hash<std::string>()( 131 fieldValue.mValue.str_value))); 132 break; 133 case FLOAT: { 134 hash = android::JenkinsHashMix(hash, 135 android::hash_type(fieldValue.mValue.float_value)); 136 break; 137 } 138 default: 139 break; 140 } 141 } 142 return JenkinsHashWhiten(hash); 143 } 144 145 bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, 146 FieldValue* output) { 147 for (const auto& value : values) { 148 if (value.mField.matches(matcherField)) { 149 (*output) = value; 150 return true; 151 } 152 } 153 return false; 154 } 155 156 bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values, 157 HashableDimensionKey* output) { 158 size_t num_matches = 0; 159 for (const auto& value : values) { 160 for (size_t i = 0; i < matcherFields.size(); ++i) { 161 const auto& matcher = matcherFields[i]; 162 if (value.mField.matches(matcher)) { 163 output->addValue(value); 164 output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); 165 output->mutableValue(num_matches)->mField.setField( 166 value.mField.getField() & matcher.mMask); 167 num_matches++; 168 } 169 } 170 } 171 return num_matches > 0; 172 } 173 174 bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) { 175 size_t num_matches = 0; 176 const int32_t simpleFieldMask = 0xff7f0000; 177 const int32_t attributionUidFieldMask = 0xff7f7f7f; 178 for (const auto& value : values) { 179 if (value.mAnnotations.isPrimaryField()) { 180 output->addValue(value); 181 output->mutableValue(num_matches)->mField.setTag(value.mField.getTag()); 182 const int32_t mask = 183 isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask; 184 output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask); 185 num_matches++; 186 } 187 } 188 return num_matches > 0; 189 } 190 191 void filterGaugeValues(const std::vector<Matcher>& matcherFields, 192 const std::vector<FieldValue>& values, std::vector<FieldValue>* output) { 193 for (const auto& field : matcherFields) { 194 for (const auto& value : values) { 195 if (value.mField.matches(field)) { 196 output->push_back(value); 197 } 198 } 199 } 200 } 201 202 void getDimensionForCondition(const std::vector<FieldValue>& eventValues, 203 const Metric2Condition& links, 204 HashableDimensionKey* conditionDimension) { 205 // Get the dimension first by using dimension from what. 206 filterValues(links.metricFields, eventValues, conditionDimension); 207 208 size_t count = conditionDimension->getValues().size(); 209 if (count != links.conditionFields.size()) { 210 return; 211 } 212 213 for (size_t i = 0; i < count; i++) { 214 conditionDimension->mutableValue(i)->mField.setField( 215 links.conditionFields[i].mMatcher.getField()); 216 conditionDimension->mutableValue(i)->mField.setTag( 217 links.conditionFields[i].mMatcher.getTag()); 218 } 219 } 220 221 void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link, 222 HashableDimensionKey* statePrimaryKey) { 223 // First, get the dimension from the event using the "what" fields from the 224 // MetricStateLinks. 225 filterValues(link.metricFields, eventValues, statePrimaryKey); 226 227 // Then check that the statePrimaryKey size equals the number of state fields 228 size_t count = statePrimaryKey->getValues().size(); 229 if (count != link.stateFields.size()) { 230 return; 231 } 232 233 // For each dimension Value in the statePrimaryKey, set the field and tag 234 // using the state atom fields from MetricStateLinks. 235 for (size_t i = 0; i < count; i++) { 236 statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField()); 237 statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag()); 238 } 239 } 240 241 bool containsLinkedStateValues(const HashableDimensionKey& whatKey, 242 const HashableDimensionKey& primaryKey, 243 const vector<Metric2State>& stateLinks, const int32_t stateAtomId) { 244 if (whatKey.getValues().size() < primaryKey.getValues().size()) { 245 ALOGE("Contains linked values false: whatKey is too small"); 246 return false; 247 } 248 249 for (const auto& primaryValue : primaryKey.getValues()) { 250 bool found = false; 251 for (const auto& whatValue : whatKey.getValues()) { 252 if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) && 253 primaryValue.mValue == whatValue.mValue) { 254 found = true; 255 break; 256 } 257 } 258 if (!found) { 259 return false; 260 } 261 } 262 return true; 263 } 264 265 bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId, 266 const Field& stateField, const Field& metricField) { 267 for (auto stateLink : stateLinks) { 268 if (stateLink.stateAtomId != stateAtomId) { 269 continue; 270 } 271 272 for (size_t i = 0; i < stateLink.stateFields.size(); i++) { 273 if (stateLink.stateFields[i].mMatcher == stateField && 274 stateLink.metricFields[i].mMatcher == metricField) { 275 return true; 276 } 277 } 278 } 279 return false; 280 } 281 282 bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) { 283 if (s1.size() != s2.size()) { 284 return s1.size() < s2.size(); 285 } 286 287 size_t count = s1.size(); 288 for (size_t i = 0; i < count; i++) { 289 if (s1[i] != s2[i]) { 290 return s1[i] < s2[i]; 291 } 292 } 293 return false; 294 } 295 296 bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const { 297 return !((*this) == that); 298 } 299 300 bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { 301 if (mValues.size() != that.getValues().size()) { 302 return false; 303 } 304 size_t count = mValues.size(); 305 for (size_t i = 0; i < count; i++) { 306 if (mValues[i] != (that.getValues())[i]) { 307 return false; 308 } 309 } 310 return true; 311 }; 312 313 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { 314 return LessThan(getValues(), that.getValues()); 315 }; 316 317 bool HashableDimensionKey::contains(const HashableDimensionKey& that) const { 318 if (mValues.size() < that.getValues().size()) { 319 return false; 320 } 321 322 if (mValues.size() == that.getValues().size()) { 323 return (*this) == that; 324 } 325 326 for (const auto& value : that.getValues()) { 327 bool found = false; 328 for (const auto& myValue : mValues) { 329 if (value.mField == myValue.mField && value.mValue == myValue.mValue) { 330 found = true; 331 break; 332 } 333 } 334 if (!found) { 335 return false; 336 } 337 } 338 339 return true; 340 } 341 342 string HashableDimensionKey::toString() const { 343 std::string output; 344 for (const auto& value : mValues) { 345 output += StringPrintf("(%d)%#x->%s ", value.mField.getTag(), value.mField.getField(), 346 value.mValue.toString().c_str()); 347 } 348 return output; 349 } 350 351 bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const { 352 return mDimensionKeyInWhat == that.getDimensionKeyInWhat() && 353 mStateValuesKey == that.getStateValuesKey(); 354 }; 355 356 string MetricDimensionKey::toString() const { 357 return mDimensionKeyInWhat.toString() + mStateValuesKey.toString(); 358 } 359 360 bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { 361 if (mDimensionKeyInWhat < that.getDimensionKeyInWhat()) { 362 return true; 363 } else if (that.getDimensionKeyInWhat() < mDimensionKeyInWhat) { 364 return false; 365 } 366 367 return mStateValuesKey < that.getStateValuesKey(); 368 } 369 370 } // namespace statsd 371 } // namespace os 372 } // namespace android 373