blob: 122de027f18d2c6c0ac78d326f624264a88eab9b [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <memory>
#include <string>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include "runtime_probe/probe_result_checker.h"
#include "runtime_probe/utils/type_utils.h"
namespace {
using ValidatorOperator = runtime_probe::ValidatorOperator;
using ReturnCode = runtime_probe::FieldConverter::ReturnCode;
constexpr const char* GetPrefix(ValidatorOperator op) {
switch (op) {
case ValidatorOperator::NOP:
return "!nop ";
case ValidatorOperator::RE:
return "!re ";
case ValidatorOperator::EQ:
return "!eq ";
case ValidatorOperator::NE:
return "!ne ";
case ValidatorOperator::GT:
return "!gt ";
case ValidatorOperator::GE:
return "!ge ";
case ValidatorOperator::LT:
return "!lt ";
case ValidatorOperator::LE:
return "!le ";
default:
DCHECK(false) << "should never reach here";
}
return nullptr;
}
constexpr const char* ToString(ValidatorOperator op) {
switch (op) {
case ValidatorOperator::NOP:
return "NOP";
case ValidatorOperator::RE:
return "RE";
case ValidatorOperator::EQ:
return "EQ";
case ValidatorOperator::NE:
return "NE";
case ValidatorOperator::GT:
return "GT";
case ValidatorOperator::GE:
return "GE";
case ValidatorOperator::LT:
return "LT";
case ValidatorOperator::LE:
return "LE";
default:
DCHECK(false) << "should never reach here";
}
return nullptr;
}
bool SplitValidateRuleString(const base::StringPiece& validate_rule,
ValidatorOperator* operator_,
base::StringPiece* operand) {
if (validate_rule.empty()) {
*operator_ = ValidatorOperator::NOP;
*operand = "";
return true;
}
auto first_space_idx = validate_rule.find_first_of(' ');
auto prefix = validate_rule.substr(0, first_space_idx + 1);
auto rest = validate_rule.substr(first_space_idx + 1);
for (int i = 0; i < static_cast<int>(ValidatorOperator::NUM_OP); i++) {
auto op = static_cast<ValidatorOperator>(i);
if (prefix == GetPrefix(op)) {
*operator_ = op;
if (op != ValidatorOperator::NOP) // NOP shouldn't have operand.
*operand = rest;
return true;
}
}
return false;
}
template <typename ConverterType>
std::unique_ptr<ConverterType> BuildNumericConverter(
const base::StringPiece& validate_rule) {
ValidatorOperator op;
base::StringPiece rest;
if (SplitValidateRuleString(validate_rule, &op, &rest)) {
if (op == ValidatorOperator::NOP)
return std::make_unique<ConverterType>(op, 0);
if (op == ValidatorOperator::EQ || op == ValidatorOperator::NE ||
op == ValidatorOperator::GT || op == ValidatorOperator::GE ||
op == ValidatorOperator::LT || op == ValidatorOperator::LE) {
typename ConverterType::OperandType operand;
if (ConverterType::StringToOperand(rest.as_string(), &operand)) {
return std::make_unique<ConverterType>(op, operand);
} else {
LOG(ERROR) << "Can't convert to operand: " << rest.as_string();
}
}
}
LOG(ERROR) << "Invalid validate rule: " << validate_rule;
return nullptr;
}
template <typename ValueType>
ReturnCode CheckNumber(ValidatorOperator op,
const ValueType lhs,
const ValueType rhs) {
bool is_valid = true;
switch (op) {
case ValidatorOperator::NOP:
break;
case ValidatorOperator::EQ:
is_valid = lhs == rhs;
break;
case ValidatorOperator::GE:
is_valid = lhs >= rhs;
break;
case ValidatorOperator::GT:
is_valid = lhs > rhs;
break;
case ValidatorOperator::LE:
is_valid = lhs <= rhs;
break;
case ValidatorOperator::LT:
is_valid = lhs < rhs;
break;
case ValidatorOperator::NE:
is_valid = lhs != rhs;
break;
default:
return ReturnCode::UNSUPPORTED_OPERATOR;
}
return is_valid ? ReturnCode::OK : ReturnCode::INVALID_VALUE;
}
} // namespace
namespace runtime_probe {
using ReturnCode = FieldConverter::ReturnCode;
std::string StringFieldConverter::ToString() const {
return base::StringPrintf("StringFieldConverter(%s, %s)",
::ToString(operator_), operand_.c_str());
}
std::string IntegerFieldConverter::ToString() const {
return base::StringPrintf("IntegerFieldConverter(%s, %d)",
::ToString(operator_), operand_);
}
std::string HexFieldConverter::ToString() const {
return base::StringPrintf("IntegerFieldConverter(%s, 0x%x)",
::ToString(operator_), operand_);
}
std::string DoubleFieldConverter::ToString() const {
return base::StringPrintf("IntegerFieldConverter(%s, %f)",
::ToString(operator_), operand_);
}
std::unique_ptr<StringFieldConverter> StringFieldConverter::Build(
const base::StringPiece& validate_rule) {
ValidatorOperator op;
base::StringPiece pattern;
if (SplitValidateRuleString(validate_rule, &op, &pattern)) {
if (op == ValidatorOperator::NOP)
return std::make_unique<StringFieldConverter>(op, "");
if (op == ValidatorOperator::EQ || op == ValidatorOperator::NE) {
return std::make_unique<StringFieldConverter>(op, pattern);
}
if (op == ValidatorOperator::RE) {
auto instance = std::make_unique<StringFieldConverter>(op, pattern);
if (instance->regex_->error().empty()) {
// No error, the pattern is valid.
return instance;
}
// Error string is set to non-empty if there are errors.
LOG(ERROR) << "Invalid pattern: " << pattern;
LOG(ERROR) << instance->regex_->error();
}
}
LOG(ERROR) << "Invalid validate rule: " << validate_rule;
return nullptr;
}
std::unique_ptr<IntegerFieldConverter> IntegerFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<IntegerFieldConverter>(validate_rule);
}
std::unique_ptr<HexFieldConverter> HexFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<HexFieldConverter>(validate_rule);
}
std::unique_ptr<DoubleFieldConverter> DoubleFieldConverter::Build(
const base::StringPiece& validate_rule) {
return BuildNumericConverter<DoubleFieldConverter>(validate_rule);
}
ReturnCode StringFieldConverter::Convert(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
const base::Value* value;
CHECK(dict_value);
if (!dict_value->Get(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetString(field_name, std::to_string(value->GetDouble()));
return ReturnCode::OK;
case base::Value::Type::INTEGER:
dict_value->SetString(field_name, std::to_string(value->GetInt()));
return ReturnCode::OK;
case base::Value::Type::NONE:
dict_value->SetString(field_name, "null");
return ReturnCode::OK;
case base::Value::Type::STRING:
return ReturnCode::OK;
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode IntegerFieldConverter::Convert(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
const base::Value* value;
CHECK(dict_value);
if (!dict_value->Get(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetInteger(field_name, value->GetDouble());
return ReturnCode::OK;
case base::Value::Type::INTEGER:
return ReturnCode::OK;
case base::Value::Type::STRING: {
std::string string_value = value->GetString();
int int_value;
if (StringToInt(string_value, &int_value)) {
dict_value->SetInteger(field_name, int_value);
return ReturnCode::OK;
} else {
LOG(ERROR) << "Failed to convert '" << string_value << "' to integer.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode HexFieldConverter::Convert(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
const base::Value* value;
CHECK(dict_value);
if (!dict_value->Get(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
dict_value->SetInteger(field_name, value->GetDouble());
return ReturnCode::OK;
case base::Value::Type::INTEGER:
return ReturnCode::OK;
case base::Value::Type::STRING: {
std::string string_value = value->GetString();
int int_value;
if (HexStringToInt(string_value, &int_value)) {
dict_value->SetInteger(field_name, int_value);
return ReturnCode::OK;
} else {
LOG(ERROR) << "Failed to convert '" << string_value << "' to integer.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode DoubleFieldConverter::Convert(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
const base::Value* value;
CHECK(dict_value);
if (!dict_value->Get(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
switch (value->type()) {
case base::Value::Type::DOUBLE:
return ReturnCode::OK;
case base::Value::Type::INTEGER:
dict_value->SetDouble(field_name, value->GetInt());
return ReturnCode::OK;
case base::Value::Type::STRING: {
std::string string_value = value->GetString();
double double_value;
if (StringToDouble(string_value, &double_value)) {
dict_value->SetDouble(field_name, double_value);
return ReturnCode::OK;
} else {
LOG(ERROR) << "Failed to convert '" << string_value << "' to double.";
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
default:
return ReturnCode::INCOMPATIBLE_VALUE;
}
}
ReturnCode StringFieldConverter::Validate(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
std::string value;
if (!dict_value->GetString(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
bool is_valid = true;
switch (operator_) {
case ValidatorOperator::NOP:
break;
case ValidatorOperator::EQ:
is_valid = value == operand_;
break;
case ValidatorOperator::RE:
is_valid = regex_->FullMatch(value);
break;
case ValidatorOperator::NE:
is_valid = value != operand_;
break;
default:
return ReturnCode::UNSUPPORTED_OPERATOR;
}
return is_valid ? ReturnCode::OK : ReturnCode::INVALID_VALUE;
}
ReturnCode IntegerFieldConverter::Validate(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
int value;
if (!dict_value->GetInteger(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
return CheckNumber(operator_, value, operand_);
}
ReturnCode HexFieldConverter::Validate(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
int value;
if (!dict_value->GetInteger(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
return CheckNumber(operator_, value, operand_);
}
ReturnCode DoubleFieldConverter::Validate(
const std::string& field_name,
base::DictionaryValue* const dict_value) const {
double value;
if (!dict_value->GetDouble(field_name, &value))
return ReturnCode::FIELD_NOT_FOUND;
return CheckNumber(operator_, value, operand_);
}
std::unique_ptr<ProbeResultChecker> ProbeResultChecker::FromDictionaryValue(
const base::DictionaryValue& dict_value) {
std::unique_ptr<ProbeResultChecker> instance{new ProbeResultChecker};
for (base::DictionaryValue::Iterator it{dict_value}; !it.IsAtEnd();
it.Advance()) {
const auto& key = it.key();
auto print_error_and_return = [&it]() {
LOG(ERROR) << "'expect' attribute should be a DictionaryValue whose "
<< "values are [<required:bool>, <expected_type:string>, "
<< "<optional_validate_rule:string>], got: " << it.value();
return nullptr;
};
const base::ListValue* list_value;
if (!it.value().GetAsList(&list_value) || list_value->GetSize() < 2 ||
list_value->GetSize() > 3) {
return print_error_and_return();
}
bool required;
if (!list_value->GetBoolean(0, &required))
return print_error_and_return();
auto* target =
required ? &instance->required_fields_ : &instance->optional_fields_;
std::string expect_type;
if (!list_value->GetString(1, &expect_type))
return print_error_and_return();
std::string validate_rule;
if (list_value->GetSize() == 3 && !list_value->GetString(2, &validate_rule))
return print_error_and_return();
if (expect_type == "str") {
(*target)[key] = StringFieldConverter::Build(validate_rule);
} else if (expect_type == "int") {
(*target)[key] = IntegerFieldConverter::Build(validate_rule);
} else if (expect_type == "double") {
(*target)[key] = DoubleFieldConverter::Build(validate_rule);
} else if (expect_type == "hex") {
(*target)[key] = HexFieldConverter::Build(validate_rule);
} else {
LOG(ERROR) << "Unknown 'expect_type': " << expect_type;
return nullptr;
}
}
return instance;
}
bool ProbeResultChecker::Apply(base::DictionaryValue* probe_result) const {
bool success = true;
CHECK(probe_result != nullptr);
// Try to convert and validate each required fields.
// Any failures will cause the final result be |false|.
for (const auto& it : required_fields_) {
if (!probe_result->HasKey(it.first)) {
LOG(ERROR) << "Missing key: " << it.first;
success = false;
break;
}
auto return_code = it.second->Convert(it.first, probe_result);
if (return_code != ReturnCode::OK) {
const base::Value* value;
probe_result->Get(it.first, &value);
LOG(ERROR) << "Failed to apply " << it.second->ToString() << " on "
<< value << "(ReturnCode = " << static_cast<int>(return_code)
<< ")";
success = false;
break;
}
}
// |ProbeStatement| will remove this element from final results, there is no
// need to continue.
if (!success) {
VLOG(3) << "probe_result = " << *probe_result;
return false;
}
// Try to convert and validate each optional fields.
// For failures, just remove them from probe_result and continue.
for (const auto& it : optional_fields_) {
if (!probe_result->HasKey(it.first))
continue;
auto return_code = it.second->Convert(it.first, probe_result);
if (return_code != ReturnCode::OK) {
VLOG(1) << "Optional field '" << it.first << "' has unexpected value, "
<< "remove it from probe result.";
probe_result->Remove(it.first, nullptr);
}
}
// Now all fields should have the correct type, let's validate them.
for (const auto& it : required_fields_) {
auto return_code = it.second->Validate(it.first, probe_result);
if (return_code != ReturnCode::OK) {
success = false;
break;
}
}
// Optional fields shouldn't have expect value.
return success;
}
} // namespace runtime_probe