//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ValueMatcher.h" #include "llvm/Support/Format.h" #include "llvm/Support/InterleavedRange.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" using namespace lldb_private; static void FormatValueDetails(llvm::raw_ostream &os, Value::ValueType value_type, Value::ContextType context_type, const Scalar &scalar, llvm::ArrayRef buffer_data) { os << "Value("; os << "value_type=" << Value::GetValueTypeAsCString(value_type); os << ", context_type=" << Value::GetContextTypeAsCString(context_type); if (value_type == Value::ValueType::HostAddress) { auto bytes_to_print = buffer_data.take_front(16); os << ", buffer=["; llvm::interleave( bytes_to_print, [&](uint8_t byte) { os << llvm::format("%02x", static_cast(byte)); }, [&]() { os << " "; }); if (buffer_data.size() > 16) os << " ..."; os << "] (" << buffer_data.size() << " bytes)"; } else { os << ", value=" << scalar; } os << ")"; } void lldb_private::PrintTo(const Value &val, std::ostream *os) { if (!os) return; llvm::raw_os_ostream raw_os(*os); FormatValueDetails(raw_os, val.GetValueType(), val.GetContextType(), val.GetScalar(), val.GetBuffer().GetData()); } bool ValueMatcher::MatchAndExplain(const Value &val, std::ostream *stream) const { if (stream) { llvm::raw_os_ostream os(*stream); return MatchAndExplainImpl(val, os); } llvm::raw_null_ostream os; return MatchAndExplainImpl(val, os); } // Match the provided value and explain any mismatches using // the raw_ostream. We use the llvm::raw_ostream here to simplify the formatting // of Scalar values which already know how to print themselves to that stream. bool ValueMatcher::MatchAndExplainImpl(const Value &val, llvm::raw_ostream &os) const { if (val.GetValueType() != m_value_type) { os << "value_type mismatch: expected " << Value::GetValueTypeAsCString(m_value_type) << ", got " << Value::GetValueTypeAsCString(val.GetValueType()) << " "; return false; } if (val.GetContextType() != m_context_type) { os << "context_type mismatch: expected " << Value::GetContextTypeAsCString(m_context_type) << ", got " << Value::GetContextTypeAsCString(val.GetContextType()) << " "; return false; } if (m_value_type == Value::ValueType::HostAddress) { const DataBufferHeap &buffer = val.GetBuffer(); const size_t buffer_size = buffer.GetByteSize(); if (buffer_size != m_expected_bytes.size()) { os << "buffer size mismatch: expected " << m_expected_bytes.size() << ", got " << buffer_size << " "; return false; } const uint8_t *data = buffer.GetBytes(); for (size_t i = 0; i < buffer_size; ++i) { if (data[i] != m_expected_bytes[i]) { os << "byte mismatch at index " << i << ": expected " << llvm::format("0x%02x", static_cast(m_expected_bytes[i])) << ", got " << llvm::format("0x%02x", static_cast(data[i])) << " "; return false; } } } else { // For Scalar, FileAddress, and LoadAddress compare m_value. const Scalar &actual_scalar = val.GetScalar(); if (actual_scalar != m_expected_scalar) { os << "scalar value mismatch: expected " << m_expected_scalar << ", got " << actual_scalar; return false; } } return true; } void ValueMatcher::DescribeTo(std::ostream *os) const { if (!os) return; llvm::raw_os_ostream raw_os(*os); FormatValueDetails(raw_os, m_value_type, m_context_type, m_expected_scalar, m_expected_bytes); } void ValueMatcher::DescribeNegationTo(std::ostream *os) const { if (!os) return; *os << "value does not match"; } testing::Matcher lldb_private::MatchScalarValue(Value::ValueType value_type, const Scalar &expected_scalar, Value::ContextType context_type) { return ValueMatcher(value_type, expected_scalar, context_type); } testing::Matcher lldb_private::MatchHostValue(Value::ValueType value_type, const std::vector &expected_bytes, Value::ContextType context_type) { return ValueMatcher(value_type, expected_bytes, context_type); } testing::Matcher lldb_private::IsScalar(const Scalar &expected_scalar, Value::ContextType context_type) { return MatchScalarValue(Value::ValueType::Scalar, expected_scalar, context_type); } testing::Matcher lldb_private::IsLoadAddress(const Scalar &expected_address, Value::ContextType context_type) { return MatchScalarValue(Value::ValueType::LoadAddress, expected_address, context_type); } testing::Matcher lldb_private::IsFileAddress(const Scalar &expected_address, Value::ContextType context_type) { return MatchScalarValue(Value::ValueType::FileAddress, expected_address, context_type); } testing::Matcher lldb_private::IsHostValue(const std::vector &expected_bytes, Value::ContextType context_type) { return MatchHostValue(Value::ValueType::HostAddress, expected_bytes, context_type); } Scalar lldb_private::GetScalar(unsigned bits, uint64_t value, bool sign) { Scalar scalar(value); scalar.TruncOrExtendTo(bits, sign); return scalar; } llvm::detail::ValueMatchesPoly> lldb_private::ExpectScalar(const Scalar &expected_scalar, Value::ContextType context_type) { return llvm::HasValue(IsScalar(expected_scalar, context_type)); } llvm::detail::ValueMatchesPoly> lldb_private::ExpectScalar(unsigned bits, uint64_t value, bool sign, Value::ContextType context_type) { return ExpectScalar(GetScalar(bits, value, sign), context_type); } llvm::detail::ValueMatchesPoly> lldb_private::ExpectLoadAddress(const Scalar &expected_address, Value::ContextType context_type) { return llvm::HasValue(IsLoadAddress(expected_address, context_type)); } llvm::detail::ValueMatchesPoly> lldb_private::ExpectFileAddress(const Scalar &expected_address, Value::ContextType context_type) { return llvm::HasValue(IsFileAddress(expected_address, context_type)); } llvm::detail::ValueMatchesPoly> lldb_private::ExpectHostAddress(const std::vector &expected_bytes, Value::ContextType context_type) { return llvm::HasValue(IsHostValue(expected_bytes, context_type)); }