blob: 461200905a2590ba39c8d1a5dc5a2ec1c7837618 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
#include "stats_util.h"
using std::ostringstream;
using std::set;
using std::string;
using std::unordered_map;
using std::vector;
namespace android {
namespace os {
namespace statsd {
bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
const vector<MatchingState>& matcherResults) {
bool matched;
switch (operation) {
case LogicalOperation::AND: {
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = false;
break;
}
}
break;
}
case LogicalOperation::OR: {
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = true;
break;
}
}
break;
}
case LogicalOperation::NOT:
matched = matcherResults[children[0]] == MatchingState::kNotMatched;
break;
case LogicalOperation::NAND:
matched = false;
for (const int childIndex : children) {
if (matcherResults[childIndex] != MatchingState::kMatched) {
matched = true;
break;
}
}
break;
case LogicalOperation::NOR:
matched = true;
for (const int childIndex : children) {
if (matcherResults[childIndex] == MatchingState::kMatched) {
matched = false;
break;
}
}
break;
case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED:
matched = false;
break;
}
return matched;
}
bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
const string& str_match) {
if (isAttributionUidField(field, value)) {
int uid = value.int_value;
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
} else if (value.getType() == STRING) {
return value.str_value == str_match;
}
return false;
}
bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
const vector<FieldValue>& values, int start, int end, int depth) {
if (depth > 2) {
ALOGE("Depth > 3 not supported");
return false;
}
if (start >= end) {
return false;
}
// Filter by entry field first
int newStart = -1;
int newEnd = end;
// because the fields are naturally sorted in the DFS order. we can safely
// break when pos is larger than the one we are searching for.
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos == matcher.field()) {
if (newStart == -1) {
newStart = i;
}
newEnd = i + 1;
} else if (pos > matcher.field()) {
break;
}
}
// Now we have zoomed in to a new range
start = newStart;
end = newEnd;
if (start == -1) {
// No such field found.
return false;
}
vector<pair<int, int>> ranges; // the ranges are for matching ANY position
if (matcher.has_position()) {
// Repeated fields position is stored as a node in the path.
depth++;
if (depth > 2) {
return false;
}
switch (matcher.position()) {
case Position::FIRST: {
for (int i = start; i < end; i++) {
int pos = values[i].mField.getPosAtDepth(depth);
if (pos != 1) {
// Again, the log elements are stored in sorted order. so
// once the position is > 1, we break;
end = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::LAST: {
// move the starting index to the first LAST field at the depth.
for (int i = start; i < end; i++) {
if (values[i].mField.isLastPos(depth)) {
start = i;
break;
}
}
ranges.push_back(std::make_pair(start, end));
break;
}
case Position::ANY: {
// ANY means all the children matchers match in any of the sub trees, it's a match
newStart = start;
newEnd = end;
// Here start is guaranteed to be a valid index.
int currentPos = values[start].mField.getPosAtDepth(depth);
// Now find all sub trees ranges.
for (int i = start; i < end; i++) {
int newPos = values[i].mField.getPosAtDepth(depth);
if (newPos != currentPos) {
ranges.push_back(std::make_pair(newStart, i));
newStart = i;
currentPos = newPos;
}
}
ranges.push_back(std::make_pair(newStart, end));
break;
}
case Position::POSITION_UNKNOWN:
break;
}
} else {
// No position
ranges.push_back(std::make_pair(start, end));
}
// start and end are still pointing to the matched range.
switch (matcher.value_matcher_case()) {
case FieldValueMatcher::kMatchesTuple: {
++depth;
// If any range matches all matchers, good.
for (const auto& range : ranges) {
bool matched = true;
for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
depth)) {
matched = false;
break;
}
}
if (matched) return true;
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqBool: {
for (int i = start; i < end; i++) {
if ((values[i].mValue.getType() == INT &&
(values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
(values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value != 0) == matcher.eq_bool())) {
return true;
}
}
return false;
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
matcher.eq_string())) {
return true;
}
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kEqInt:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(matcher.eq_int() == values[i].mValue.int_value)) {
return true;
}
// eq_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(matcher.eq_int() == values[i].mValue.long_value)) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kLtInt:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value < matcher.lt_int())) {
return true;
}
// lt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value < matcher.lt_int())) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kGtInt:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value > matcher.gt_int())) {
return true;
}
// gt_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value > matcher.gt_int())) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kLtFloat:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value < matcher.lt_float())) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kGtFloat:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == FLOAT &&
(values[i].mValue.float_value > matcher.gt_float())) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kLteInt:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value <= matcher.lte_int())) {
return true;
}
// lte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value <= matcher.lte_int())) {
return true;
}
}
return false;
case FieldValueMatcher::ValueMatcherCase::kGteInt:
for (int i = start; i < end; i++) {
if (values[i].mValue.getType() == INT &&
(values[i].mValue.int_value >= matcher.gte_int())) {
return true;
}
// gte_int covers both int and long.
if (values[i].mValue.getType() == LONG &&
(values[i].mValue.long_value >= matcher.gte_int())) {
return true;
}
}
return false;
default:
return false;
}
}
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
if (simpleMatcher.field_value_matcher_size() <= 0) {
return event.GetTagId() == simpleMatcher.atom_id();
}
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
}
}
return true;
}
} // namespace statsd
} // namespace os
} // namespace android