<commit_before>//===- lib/Clang/MappedValue.cpp ------------------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "seec-clang"

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedModule.hpp"
#include "seec/Clang/MappedStmt.hpp"
#include "seec/Clang/MappedValue.hpp"
#include "seec/Clang/TypeMatch.hpp"
#include "seec/Trace/MemoryState.hpp"
#include "seec/Trace/ProcessState.hpp"
#include "seec/Trace/StreamState.hpp"
#include "seec/Trace/ThreadState.hpp"
#include "seec/Trace/FunctionState.hpp"
#include "seec/Trace/GetRecreatedValue.hpp"
#include "seec/Util/Fallthrough.hpp"
#include "seec/Util/Maybe.hpp"
#include "seec/Util/Range.hpp"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Frontend/ASTUnit.h"

#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

#include <cctype>
#include <string>


using namespace std;


namespace seec {

namespace cm {


/// \brief Used to store the initialization state of a Value.
///
enum class InitializationState {
  None,
  Partial,
  Complete
};


//===----------------------------------------------------------------------===//
// Value
//===----------------------------------------------------------------------===//

Value::~Value() = default;


//===----------------------------------------------------------------------===//
// ValueStoreImpl
//===----------------------------------------------------------------------===//

class TypedValueSet {
  std::vector<std::pair<MatchType, std::shared_ptr<Value const>>> Items;

public:
  TypedValueSet()
  : Items()
  {}

  std::shared_ptr<Value const> getShared(MatchType const &ForType) const {
    for (auto const &Pair : Items)
      if (Pair.first == ForType)
        return Pair.second;

    return std::shared_ptr<Value const>{};
  }

  std::shared_ptr<Value const> getSharedFromTypeString(llvm::StringRef TS) const
  {
    for (auto const &Pair : Items)
      if (Pair.second->getTypeAsString() == TS)
        return Pair.second;

    return std::shared_ptr<Value const>();
  }

  Value const *get(MatchType const &ForType) const {
    for (auto const &Pair : Items)
      if (Pair.first == ForType)
        return Pair.second.get();

    return nullptr;
  }

  void add(MatchType const &ForType, std::shared_ptr<Value const> Val) {
    Items.emplace_back(ForType, std::move(Val));
  }
};

class ValueStoreImpl final {
  /// Control access to the Store variable.
  mutable std::mutex StoreAccess;

  // Two-stage lookup to find previously created Value objects.
  // The first stage is the in-memory address of the object.
  // The second stage is the canonical type of the object.
  mutable llvm::DenseMap<stateptr_ty, TypedValueSet> Store;

  /// SeeC-Clang mapping information.
  seec::seec_clang::MappedModule const &Mapping;

  // Disable copying and moving.
  ValueStoreImpl(ValueStoreImpl const &) = delete;
  ValueStoreImpl(ValueStoreImpl &&) = delete;
  ValueStoreImpl &operator=(ValueStoreImpl const &) = delete;
  ValueStoreImpl &operator=(ValueStore &&) = delete;

public:
  /// \brief Constructor.
  ValueStoreImpl(seec::seec_clang::MappedModule const &WithMapping)
  : StoreAccess(),
    Store(),
    Mapping(WithMapping)
  {}

  /// \brief Find or construct a Value for the given type.
  ///
  std::shared_ptr<Value const>
  getValue(std::shared_ptr<ValueStore const> StorePtr,
           ::clang::QualType QualType,
           ::clang::ASTContext const &ASTContext,
           stateptr_ty Address,
           seec::trace::ProcessState const &ProcessState,
           seec::trace::FunctionState const *OwningFunction) const;

  /// \brief Get SeeC-Clang mapping information.
  ///
  seec::seec_clang::MappedModule const &getMapping() const { return Mapping; }

  /// \brief Find first \c Value matching the given predicate.
  ///
  std::shared_ptr<Value const>
  findFromAddressAndType(stateptr_ty Address, llvm::StringRef TypeString) const
  {
    auto const It = Store.find(Address);

    if (It == Store.end())
      return std::shared_ptr<Value const>();

    return It->second.getSharedFromTypeString(TypeString);
  }
};


//===----------------------------------------------------------------------===//
// readAPIntFromMemory()
//===----------------------------------------------------------------------===//

Maybe<llvm::APInt> readAPIntFromMemory(clang::ASTContext const &AST,
                                       clang::Type const *Type,
                                       stateptr_ty const Address,
                                       seec::trace::MemoryState const &Memory)
{
  auto const Size = AST.getTypeSizeInChars(Type);
  auto const Region = Memory.getRegion(MemoryArea(Address, Size.getQuantity()));
  if (!Region.isAllocated() || !Region.isCompletelyInitialized())
    return Maybe<llvm::APInt>();

  auto const BitWidth = AST.getTypeSize(Type);
  auto const RawBytes = Region.getByteValues();
  auto const Data = RawBytes.data();

  switch (BitWidth) {
    case 8:  return llvm::APInt(8,  *reinterpret_cast<uint8_t  const *>(Data));
    case 16: return llvm::APInt(16, *reinterpret_cast<uint16_t const *>(Data));
    case 32: return llvm::APInt(32, *reinterpret_cast<uint32_t const *>(Data));
    case 64: return llvm::APInt(64, *reinterpret_cast<uint64_t const *>(Data));
  }

  llvm::errs() << "readAPIntFromMemory: unsupported bitwidth " << BitWidth
               << "\n";
  return Maybe<llvm::APInt>();
}


//===----------------------------------------------------------------------===//
// getScalarValueAsString() - from memory
//===----------------------------------------------------------------------===//

std::string
getScalarValueAsString(clang::ASTContext const &AST,
                       clang::BuiltinType const *Type,
                       stateptr_ty const Address,
                       seec::trace::MemoryState const &Memory)
{
  if (Type->getKind() == clang::BuiltinType::Char_S
      || Type->getKind() == clang::BuiltinType::Char_U)
  {
    // Special handling to pretty-print chars.
    auto const Region = Memory.getRegion(MemoryArea(Address, 1));
    if (!Region.isAllocated() || !Region.isCompletelyInitialized())
      return std::string{"<uninitialized>"};

    auto const Value = Region.getByteValues()[0];
    std::string Printed;

    if ((static_cast<uint8_t>(Value) & 128) == 0) {
      static char const * const FormattedASCII[] = {
        "\\0", "soh", "stx", "etx", "eot", "enq", "ack", "bel",  "bs", "\\t",
        "\\n",  "vt", "\\f", "\\r",  "so",  "si", "dle", "dc1", "dc2", "dc3",
        "dc4", "nak", "syn", "etb", "can",  "em", "sub", "esc",  "fs",  "gs",
         "rs",  "us",  "sp",   "!",  "\"",   "#",   "$",   "%",   "&",   "'",
          "(",   ")",   "*",   "+",   ",",   "-",   ".",   "/",   "0",   "1",
          "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",   ":",   ";",
          "<",   "=",   ">",   "?",   "@",   "A",   "B",   "C",   "D",   "E",
          "F",   "G",   "H",   "I",   "J",   "K",   "L",   "M",   "N",   "O",
          "P",   "Q",   "R",   "S",   "T",   "U",   "V",   "W",   "X",   "Y",
          "Z",   "[",   "\\",  "]",   "^",   "_",   "`",   "a",   "b",   "c",
          "d",   "e",   "f",   "g",   "h",   "i",   "j",   "k",   "l",   "m",
          "n",   "o",   "p",   "q",   "r",   "s",   "t",   "u",   "v",   "w",
          "x",   "y",   "z",   "{",   "|",   "}",   "~", "del" };

      Printed = FormattedASCII[static_cast<unsigned>(Value)];
    }
    else if (Type->getKind() == clang::BuiltinType::Char_S) {
      Printed = std::to_string(static_cast<signed char>(Value));
    }
    else {
      Printed = std::to_string(static_cast<unsigned char>(Value));
    }

    return Printed;
  }
  else if (Type->isInteger()) {
    auto const Size     = AST.getTypeSizeInChars(Type);
    auto const Region   = Memory.getRegion(MemoryArea(Address,
                                                      Size.getQuantity()));

    if (!Region.isAllocated() || !Region.isCompletelyInitialized())
      return std::string{"<unallocated or uninitialized>"};

    auto const BitWidth = AST.getTypeSize(Type);
    auto const NWords   = BitWidth / 64 + (BitWidth % 64 ? 1 : 0);

    uint64_t Words[NWords];
    std::memcpy(reinterpret_cast<char *>(Words),
                Region.getByteValues().data(),
                Size.getQuantity());

    llvm::APSInt Value(llvm::APInt(BitWidth,
                                   llvm::ArrayRef<uint64_t>(Words, NWords)),
                       Type->isUnsignedInteger());

    llvm::SmallString<32> Buffer;
    Value.toString(Buffer);
    return Buffer.str().str();
  }
  else if (Type->isFloatingPoint()) {
    auto const &Semantics = AST.getFloatTypeSemantics(clang::QualType(Type, 0));
<commit_msg>Handle endianness in getScalarValueAsString.<commit_after>//===- lib/Clang/MappedValue.cpp ------------------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "seec-clang"

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedModule.hpp"
#include "seec/Clang/MappedStmt.hpp"
#include "seec/Clang/MappedValue.hpp"
#include "seec/Clang/TypeMatch.hpp"
#include "seec/Trace/MemoryState.hpp"
#include "seec/Trace/ProcessState.hpp"
#include "seec/Trace/StreamState.hpp"
#include "seec/Trace/ThreadState.hpp"
#include "seec/Trace/FunctionState.hpp"
#include "seec/Trace/GetRecreatedValue.hpp"
#include "seec/Util/Fallthrough.hpp"
#include "seec/Util/Maybe.hpp"
#include "seec/Util/Range.hpp"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/ASTUnit.h"

#include "llvm/Support/Debug.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"

#include <cctype>
#include <string>


using namespace std;


namespace seec {

namespace cm {


/// \brief Used to store the initialization state of a Value.
///
enum class InitializationState {
  None,
  Partial,
  Complete
};


//===----------------------------------------------------------------------===//
// Value
//===----------------------------------------------------------------------===//

Value::~Value() = default;


//===----------------------------------------------------------------------===//
// ValueStoreImpl
//===----------------------------------------------------------------------===//

class TypedValueSet {
  std::vector<std::pair<MatchType, std::shared_ptr<Value const>>> Items;

public:
  TypedValueSet()
  : Items()
  {}

  std::shared_ptr<Value const> getShared(MatchType const &ForType) const {
    for (auto const &Pair : Items)
      if (Pair.first == ForType)
        return Pair.second;

    return std::shared_ptr<Value const>{};
  }

  std::shared_ptr<Value const> getSharedFromTypeString(llvm::StringRef TS) const
  {
    for (auto const &Pair : Items)
      if (Pair.second->getTypeAsString() == TS)
        return Pair.second;

    return std::shared_ptr<Value const>();
  }

  Value const *get(MatchType const &ForType) const {
    for (auto const &Pair : Items)
      if (Pair.first == ForType)
        return Pair.second.get();

    return nullptr;
  }

  void add(MatchType const &ForType, std::shared_ptr<Value const> Val) {
    Items.emplace_back(ForType, std::move(Val));
  }
};

class ValueStoreImpl final {
  /// Control access to the Store variable.
  mutable std::mutex StoreAccess;

  // Two-stage lookup to find previously created Value objects.
  // The first stage is the in-memory address of the object.
  // The second stage is the canonical type of the object.
  mutable llvm::DenseMap<stateptr_ty, TypedValueSet> Store;

  /// SeeC-Clang mapping information.
  seec::seec_clang::MappedModule const &Mapping;

  // Disable copying and moving.
  ValueStoreImpl(ValueStoreImpl const &) = delete;
  ValueStoreImpl(ValueStoreImpl &&) = delete;
  ValueStoreImpl &operator=(ValueStoreImpl const &) = delete;
  ValueStoreImpl &operator=(ValueStore &&) = delete;

public:
  /// \brief Constructor.
  ValueStoreImpl(seec::seec_clang::MappedModule const &WithMapping)
  : StoreAccess(),
    Store(),
    Mapping(WithMapping)
  {}

  /// \brief Find or construct a Value for the given type.
  ///
  std::shared_ptr<Value const>
  getValue(std::shared_ptr<ValueStore const> StorePtr,
           ::clang::QualType QualType,
           ::clang::ASTContext const &ASTContext,
           stateptr_ty Address,
           seec::trace::ProcessState const &ProcessState,
           seec::trace::FunctionState const *OwningFunction) const;

  /// \brief Get SeeC-Clang mapping information.
  ///
  seec::seec_clang::MappedModule const &getMapping() const { return Mapping; }

  /// \brief Find first \c Value matching the given predicate.
  ///
  std::shared_ptr<Value const>
  findFromAddressAndType(stateptr_ty Address, llvm::StringRef TypeString) const
  {
    auto const It = Store.find(Address);

    if (It == Store.end())
      return std::shared_ptr<Value const>();

    return It->second.getSharedFromTypeString(TypeString);
  }
};


//===----------------------------------------------------------------------===//
// readAPIntFromMemory()
//===----------------------------------------------------------------------===//

Maybe<llvm::APInt> readAPIntFromMemory(clang::ASTContext const &AST,
                                       clang::Type const *Type,
                                       stateptr_ty const Address,
                                       seec::trace::MemoryState const &Memory)
{
  auto const Size = AST.getTypeSizeInChars(Type);
  auto const Region = Memory.getRegion(MemoryArea(Address, Size.getQuantity()));
  if (!Region.isAllocated() || !Region.isCompletelyInitialized())
    return Maybe<llvm::APInt>();

  auto const BitWidth = AST.getTypeSize(Type);
  auto const RawBytes = Region.getByteValues();
  auto const Data = RawBytes.data();

  switch (BitWidth) {
    case 8:  return llvm::APInt(8,  *reinterpret_cast<uint8_t  const *>(Data));
    case 16: return llvm::APInt(16, *reinterpret_cast<uint16_t const *>(Data));
    case 32: return llvm::APInt(32, *reinterpret_cast<uint32_t const *>(Data));
    case 64: return llvm::APInt(64, *reinterpret_cast<uint64_t const *>(Data));
  }

  llvm::errs() << "readAPIntFromMemory: unsupported bitwidth " << BitWidth
               << "\n";
  return Maybe<llvm::APInt>();
}


//===----------------------------------------------------------------------===//
// getScalarValueAsString() - from memory
//===----------------------------------------------------------------------===//

std::string
getScalarValueAsString(clang::ASTContext const &AST,
                       clang::BuiltinType const *Type,
                       stateptr_ty const Address,
                       seec::trace::MemoryState const &Memory)
{
  if (Type->getKind() == clang::BuiltinType::Char_S
      || Type->getKind() == clang::BuiltinType::Char_U)
  {
    // Special handling to pretty-print chars.
    auto const Region = Memory.getRegion(MemoryArea(Address, 1));
    if (!Region.isAllocated() || !Region.isCompletelyInitialized())
      return std::string{"<uninitialized>"};

    auto const Value = Region.getByteValues()[0];
    std::string Printed;

    if ((static_cast<uint8_t>(Value) & 128) == 0) {
      static char const * const FormattedASCII[] = {
        "\\0", "soh", "stx", "etx", "eot", "enq", "ack", "bel",  "bs", "\\t",
        "\\n",  "vt", "\\f", "\\r",  "so",  "si", "dle", "dc1", "dc2", "dc3",
        "dc4", "nak", "syn", "etb", "can",  "em", "sub", "esc",  "fs",  "gs",
         "rs",  "us",  "sp",   "!",  "\"",   "#",   "$",   "%",   "&",   "'",
          "(",   ")",   "*",   "+",   ",",   "-",   ".",   "/",   "0",   "1",
          "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",   ":",   ";",
          "<",   "=",   ">",   "?",   "@",   "A",   "B",   "C",   "D",   "E",
          "F",   "G",   "H",   "I",   "J",   "K",   "L",   "M",   "N",   "O",
          "P",   "Q",   "R",   "S",   "T",   "U",   "V",   "W",   "X",   "Y",
          "Z",   "[",   "\\",  "]",   "^",   "_",   "`",   "a",   "b",   "c",
          "d",   "e",   "f",   "g",   "h",   "i",   "j",   "k",   "l",   "m",
          "n",   "o",   "p",   "q",   "r",   "s",   "t",   "u",   "v",   "w",
          "x",   "y",   "z",   "{",   "|",   "}",   "~", "del" };

      Printed = FormattedASCII[static_cast<unsigned>(Value)];
    }
    else if (Type->getKind() == clang::BuiltinType::Char_S) {
      Printed = std::to_string(static_cast<signed char>(Value));
    }
    else {
      Printed = std::to_string(static_cast<unsigned char>(Value));
    }

    return Printed;
  }
  else if (Type->isInteger()) {
    auto const Size     = AST.getTypeSizeInChars(Type);
    auto const Region   = Memory.getRegion(MemoryArea(Address,
                                                      Size.getQuantity()));

    if (!Region.isAllocated() || !Region.isCompletelyInitialized())
      return std::string{"<unallocated or uninitialized>"};

    auto const BitWidth = AST.getTypeSize(Type);
    auto const NWords   = BitWidth / 64 + (BitWidth % 64 ? 1 : 0);

    uint64_t Words[NWords];
    std::memcpy(reinterpret_cast<char *>(Words),
                Region.getByteValues().data(),
                Size.getQuantity());

    llvm::SmallString<32> Buffer;

    if (AST.getTargetInfo().isBigEndian() == llvm::sys::IsBigEndianHost) {
      // Host and recorder have the same endianness.
      llvm::APSInt Value(llvm::APInt(BitWidth,
                                     llvm::ArrayRef<uint64_t>(Words, NWords)),
                         Type->isUnsignedInteger());
      Value.toString(Buffer);
    }
    else {
      // Recorder's representation must be byte-swapped to match current host.
      llvm::APSInt Value(llvm::APInt(BitWidth,
                                     llvm::ArrayRef<uint64_t>(Words, NWords))
                                    .byteSwap(),
                         Type->isUnsignedInteger());
      Value.toString(Buffer);
    }

    return Buffer.str().str();
  }
  else if (Type->isFloatingPoint()) {
    auto const &Semantics = AST.getFloatTypeSemantics(clang::QualType(Type, 0));

{'fraction_non_alphanumeric': 0.24272051009564294, 'fraction_numerical': 0.006057385759829968, 'mean_word_length': 2.0653094462540715, 'pattern_counts': {'":': 2, '<': 80, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4194999', 'n_tokens_mistral': 5977, 'n_tokens_neox': 5781, 'n_words': 1244}