Home | History | Annotate | Download | only in condition
      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 
     17 #define DEBUG false  // STOPSHIP if true
     18 #include "Log.h"
     19 
     20 #include "SimpleConditionTracker.h"
     21 #include "guardrail/StatsdStats.h"
     22 
     23 namespace android {
     24 namespace os {
     25 namespace statsd {
     26 
     27 using std::map;
     28 using std::string;
     29 using std::unique_ptr;
     30 using std::unordered_map;
     31 using std::vector;
     32 
     33 SimpleConditionTracker::SimpleConditionTracker(
     34         const ConfigKey& key, const int64_t& id, const int index,
     35         const SimplePredicate& simplePredicate,
     36         const unordered_map<int64_t, int>& trackerNameIndexMap)
     37     : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
     38     VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
     39     mCountNesting = simplePredicate.count_nesting();
     40 
     41     if (simplePredicate.has_start()) {
     42         auto pair = trackerNameIndexMap.find(simplePredicate.start());
     43         if (pair == trackerNameIndexMap.end()) {
     44             ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
     45             return;
     46         }
     47         mStartLogMatcherIndex = pair->second;
     48         mTrackerIndex.insert(mStartLogMatcherIndex);
     49     } else {
     50         mStartLogMatcherIndex = -1;
     51     }
     52 
     53     if (simplePredicate.has_stop()) {
     54         auto pair = trackerNameIndexMap.find(simplePredicate.stop());
     55         if (pair == trackerNameIndexMap.end()) {
     56             ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
     57             return;
     58         }
     59         mStopLogMatcherIndex = pair->second;
     60         mTrackerIndex.insert(mStopLogMatcherIndex);
     61     } else {
     62         mStopLogMatcherIndex = -1;
     63     }
     64 
     65     if (simplePredicate.has_stop_all()) {
     66         auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
     67         if (pair == trackerNameIndexMap.end()) {
     68             ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
     69             return;
     70         }
     71         mStopAllLogMatcherIndex = pair->second;
     72         mTrackerIndex.insert(mStopAllLogMatcherIndex);
     73     } else {
     74         mStopAllLogMatcherIndex = -1;
     75     }
     76 
     77     if (simplePredicate.has_dimensions()) {
     78         translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
     79         if (mOutputDimensions.size() > 0) {
     80             mSliced = true;
     81             mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
     82         }
     83         mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
     84     }
     85 
     86     if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
     87         mInitialValue = ConditionState::kFalse;
     88     } else {
     89         mInitialValue = ConditionState::kUnknown;
     90     }
     91 
     92     mNonSlicedConditionState = mInitialValue;
     93 
     94     if (!mSliced) {
     95         mUnSlicedPart = mInitialValue;
     96     }
     97 
     98     mInitialized = true;
     99 }
    100 
    101 SimpleConditionTracker::~SimpleConditionTracker() {
    102     VLOG("~SimpleConditionTracker()");
    103 }
    104 
    105 bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
    106                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
    107                                   const unordered_map<int64_t, int>& conditionIdIndexMap,
    108                                   vector<bool>& stack) {
    109     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
    110     // if the initialization was successful.
    111     return mInitialized;
    112 }
    113 
    114 void SimpleConditionTracker::dumpState() {
    115     VLOG("%lld DUMP:", (long long)mConditionId);
    116     for (const auto& pair : mSlicedConditionState) {
    117         VLOG("\t%s : %d", pair.first.toString().c_str(), pair.second);
    118     }
    119 
    120     VLOG("Changed to true keys: \n");
    121     for (const auto& key : mLastChangedToTrueDimensions) {
    122         VLOG("%s", key.toString().c_str());
    123     }
    124     VLOG("Changed to false keys: \n");
    125     for (const auto& key : mLastChangedToFalseDimensions) {
    126         VLOG("%s", key.toString().c_str());
    127     }
    128 }
    129 
    130 void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
    131                                            std::vector<bool>& conditionChangedCache) {
    132     // Unless the default condition is false, and there was nothing started, otherwise we have
    133     // triggered a condition change.
    134     conditionChangedCache[mIndex] =
    135             (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
    136                                                                                            : true;
    137 
    138     for (const auto& cond : mSlicedConditionState) {
    139         if (cond.second > 0) {
    140             mLastChangedToFalseDimensions.insert(cond.first);
    141         }
    142     }
    143 
    144     // After StopAll, we know everything has stopped. From now on, default condition is false.
    145     mInitialValue = ConditionState::kFalse;
    146     mSlicedConditionState.clear();
    147     conditionCache[mIndex] = ConditionState::kFalse;
    148     if (!mSliced) {
    149         mUnSlicedPart = ConditionState::kFalse;
    150     }
    151 }
    152 
    153 bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
    154     if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
    155         // if the condition is not sliced or the key is not new, we are good!
    156         return false;
    157     }
    158     // 1. Report the tuple count if the tuple count > soft limit
    159     if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
    160         size_t newTupleCount = mSlicedConditionState.size() + 1;
    161         StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
    162         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
    163         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
    164             ALOGE("Predicate %lld dropping data for dimension key %s",
    165                 (long long)mConditionId, newKey.toString().c_str());
    166             return true;
    167         }
    168     }
    169     return false;
    170 }
    171 
    172 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
    173                                                   bool matchStart, ConditionState* conditionCache,
    174                                                   bool* conditionChangedCache) {
    175     bool changed = false;
    176     auto outputIt = mSlicedConditionState.find(outputKey);
    177     ConditionState newCondition;
    178     if (hitGuardRail(outputKey)) {
    179         (*conditionChangedCache) = false;
    180         // Tells the caller it's evaluated.
    181         (*conditionCache) = ConditionState::kUnknown;
    182         return;
    183     }
    184     if (outputIt == mSlicedConditionState.end()) {
    185         // We get a new output key.
    186         newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
    187         if (matchStart && mInitialValue != ConditionState::kTrue) {
    188             mSlicedConditionState[outputKey] = 1;
    189             changed = true;
    190             mLastChangedToTrueDimensions.insert(outputKey);
    191         } else if (mInitialValue != ConditionState::kFalse) {
    192             // it's a stop and we don't have history about it.
    193             // If the default condition is not false, it means this stop is valuable to us.
    194             mSlicedConditionState[outputKey] = 0;
    195             mLastChangedToFalseDimensions.insert(outputKey);
    196             changed = true;
    197         }
    198     } else {
    199         // we have history about this output key.
    200         auto& startedCount = outputIt->second;
    201         // assign the old value first.
    202         newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    203         if (matchStart) {
    204             if (startedCount == 0) {
    205                 mLastChangedToTrueDimensions.insert(outputKey);
    206                 // This condition for this output key will change from false -> true
    207                 changed = true;
    208             }
    209 
    210             // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
    211             // as 1 if not counting nesting.
    212             startedCount++;
    213             newCondition = ConditionState::kTrue;
    214         } else {
    215             // This is a stop event.
    216             if (startedCount > 0) {
    217                 if (mCountNesting) {
    218                     startedCount--;
    219                     if (startedCount == 0) {
    220                         newCondition = ConditionState::kFalse;
    221                     }
    222                 } else {
    223                     // not counting nesting, so ignore the number of starts, stop now.
    224                     startedCount = 0;
    225                     newCondition = ConditionState::kFalse;
    226                 }
    227                 // if everything has stopped for this output key, condition true -> false;
    228                 if (startedCount == 0) {
    229                     mLastChangedToFalseDimensions.insert(outputKey);
    230                     changed = true;
    231                 }
    232             }
    233 
    234             // if default condition is false, it means we don't need to keep the false values.
    235             if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
    236                 mSlicedConditionState.erase(outputIt);
    237                 VLOG("erase key %s", outputKey.toString().c_str());
    238             }
    239         }
    240     }
    241 
    242     // dump all dimensions for debugging
    243     if (DEBUG) {
    244         dumpState();
    245     }
    246 
    247     (*conditionChangedCache) = changed;
    248     (*conditionCache) = newCondition;
    249 
    250     VLOG("SimplePredicate %lld nonSlicedChange? %d", (long long)mConditionId,
    251          conditionChangedCache[mIndex] == true);
    252 }
    253 
    254 void SimpleConditionTracker::evaluateCondition(
    255         const LogEvent& event,
    256         const vector<MatchingState>& eventMatcherValues,
    257         const vector<sp<ConditionTracker>>& mAllConditions,
    258         vector<ConditionState>& conditionCache,
    259         vector<bool>& conditionChangedCache) {
    260     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
    261         // it has been evaluated.
    262         VLOG("Yes, already evaluated, %lld %d",
    263             (long long)mConditionId, conditionCache[mIndex]);
    264         return;
    265     }
    266     mLastChangedToTrueDimensions.clear();
    267     mLastChangedToFalseDimensions.clear();
    268 
    269     if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
    270         eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
    271         handleStopAll(conditionCache, conditionChangedCache);
    272         return;
    273     }
    274 
    275     int matchedState = -1;
    276     // Note: The order to evaluate the following start, stop, stop_all matters.
    277     // The priority of overwrite is stop_all > stop > start.
    278     if (mStartLogMatcherIndex >= 0 &&
    279         eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
    280         matchedState = 1;
    281     }
    282 
    283     if (mStopLogMatcherIndex >= 0 &&
    284         eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
    285         matchedState = 0;
    286     }
    287 
    288     if (matchedState < 0) {
    289         // The event doesn't match this condition. So we just report existing condition values.
    290         conditionChangedCache[mIndex] = false;
    291         if (mSliced) {
    292             // if the condition result is sliced. The overall condition is true if any of the sliced
    293             // condition is true
    294             conditionCache[mIndex] = mInitialValue;
    295             for (const auto& slicedCondition : mSlicedConditionState) {
    296                 if (slicedCondition.second > 0) {
    297                     conditionCache[mIndex] = ConditionState::kTrue;
    298                     break;
    299                 }
    300             }
    301         } else {
    302             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
    303             if (itr == mSlicedConditionState.end()) {
    304                 // condition not sliced, but we haven't seen the matched start or stop yet. so
    305                 // return initial value.
    306                 conditionCache[mIndex] = mInitialValue;
    307             } else {
    308                 // return the cached condition.
    309                 conditionCache[mIndex] =
    310                         itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    311             }
    312             mUnSlicedPart = conditionCache[mIndex];
    313         }
    314 
    315         return;
    316     }
    317 
    318     ConditionState overallState = mInitialValue;
    319     bool overallChanged = false;
    320 
    321     if (mOutputDimensions.size() == 0) {
    322         handleConditionEvent(DEFAULT_DIMENSION_KEY, matchedState == 1, &overallState,
    323                              &overallChanged);
    324     } else if (!mContainANYPositionInInternalDimensions) {
    325         HashableDimensionKey outputValue;
    326         filterValues(mOutputDimensions, event.getValues(), &outputValue);
    327 
    328         // If this event has multiple nodes in the attribution chain,  this log event probably will
    329         // generate multiple dimensions. If so, we will find if the condition changes for any
    330         // dimension and ask the corresponding metric producer to verify whether the actual sliced
    331         // condition has changed or not.
    332         // A high level assumption is that a predicate is either sliced or unsliced. We will never
    333         // have both sliced and unsliced version of a predicate.
    334         handleConditionEvent(outputValue, matchedState == 1, &overallState, &overallChanged);
    335     } else {
    336         ALOGE("The condition tracker should not be sliced by ANY position matcher.");
    337     }
    338     conditionCache[mIndex] = overallState;
    339     conditionChangedCache[mIndex] = overallChanged;
    340     if (!mSliced) {
    341         mUnSlicedPart = overallState;
    342     }
    343 }
    344 
    345 void SimpleConditionTracker::isConditionMet(
    346         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
    347         const vector<Matcher>& dimensionFields,
    348         const bool isSubOutputDimensionFields,
    349         const bool isPartialLink,
    350         vector<ConditionState>& conditionCache,
    351         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
    352 
    353     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
    354         // it has been evaluated.
    355         VLOG("Yes, already evaluated, %lld %d",
    356             (long long)mConditionId, conditionCache[mIndex]);
    357         return;
    358     }
    359     const auto pair = conditionParameters.find(mConditionId);
    360 
    361     if (pair == conditionParameters.end()) {
    362         ConditionState conditionState = ConditionState::kNotEvaluated;
    363         if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
    364             conditionState = conditionState | getMetConditionDimension(
    365                 allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
    366         } else {
    367             conditionState = conditionState | mInitialValue;
    368             if (!mSliced) {
    369                 const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
    370                 if (itr != mSlicedConditionState.end()) {
    371                     ConditionState sliceState =
    372                         itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    373                     conditionState = conditionState | sliceState;
    374                 }
    375             }
    376         }
    377         conditionCache[mIndex] = conditionState;
    378         return;
    379     }
    380 
    381     ConditionState conditionState = ConditionState::kNotEvaluated;
    382     const HashableDimensionKey& key = pair->second;
    383     if (isPartialLink) {
    384         // For unseen key, check whether the require dimensions are subset of sliced condition
    385         // output.
    386         conditionState = conditionState | mInitialValue;
    387         for (const auto& slice : mSlicedConditionState) {
    388             ConditionState sliceState =
    389                 slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    390             if (slice.first.contains(key)) {
    391                 conditionState = conditionState | sliceState;
    392                 if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
    393                     if (isSubOutputDimensionFields) {
    394                         HashableDimensionKey dimensionKey;
    395                         filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
    396                         dimensionsKeySet.insert(dimensionKey);
    397                     } else {
    398                         dimensionsKeySet.insert(slice.first);
    399                     }
    400                 }
    401             }
    402         }
    403     } else {
    404         auto startedCountIt = mSlicedConditionState.find(key);
    405         conditionState = conditionState | mInitialValue;
    406         if (startedCountIt != mSlicedConditionState.end()) {
    407             ConditionState sliceState =
    408                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    409             conditionState = conditionState | sliceState;
    410             if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
    411                 if (isSubOutputDimensionFields) {
    412                     HashableDimensionKey dimensionKey;
    413                     filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
    414                     dimensionsKeySet.insert(dimensionKey);
    415                 } else {
    416                     dimensionsKeySet.insert(startedCountIt->first);
    417                 }
    418             }
    419         }
    420 
    421     }
    422     conditionCache[mIndex] = conditionState;
    423     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
    424 }
    425 
    426 ConditionState SimpleConditionTracker::getMetConditionDimension(
    427         const std::vector<sp<ConditionTracker>>& allConditions,
    428         const vector<Matcher>& dimensionFields,
    429         const bool isSubOutputDimensionFields,
    430         std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
    431     ConditionState conditionState = mInitialValue;
    432     if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
    433         dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
    434         const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
    435         if (itr != mSlicedConditionState.end()) {
    436             ConditionState sliceState =
    437                 itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    438             conditionState = conditionState | sliceState;
    439         }
    440         return conditionState;
    441     }
    442 
    443     for (const auto& slice : mSlicedConditionState) {
    444         ConditionState sliceState =
    445             slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
    446         conditionState = conditionState | sliceState;
    447 
    448         if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
    449             if (isSubOutputDimensionFields) {
    450                 HashableDimensionKey dimensionKey;
    451                 filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
    452                 dimensionsKeySet.insert(dimensionKey);
    453             } else {
    454                 dimensionsKeySet.insert(slice.first);
    455             }
    456         }
    457     }
    458     return conditionState;
    459 }
    460 
    461 }  // namespace statsd
    462 }  // namespace os
    463 }  // namespace android
    464