<commit_before>#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
#define ENTT_CORE_COMPRESSED_PAIR_HPP

#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include "type_traits.hpp"

namespace entt {

/**
 * @cond TURN_OFF_DOXYGEN
 * Internal details not to be documented.
 */

namespace internal {

template<typename Type, std::size_t, typename = void>
struct compressed_pair_element {
    using reference = Type &;
    using const_reference = const Type &;

    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
        : value{} {}

    template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
    constexpr compressed_pair_element(Args &&args) noexcept(std::is_nothrow_constructible_v<Type, Args>)
        : value{std::forward<Args>(args)} {}

    template<typename... Args, std::size_t... Index>
    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
        : value{std::forward<Args>(std::get<Index>(args))...} {}

    [[nodiscard]] constexpr reference get() noexcept {
        return value;
    }

    [[nodiscard]] constexpr const_reference get() const noexcept {
        return value;
    }

private:
    Type value;
};

template<typename Type, std::size_t Tag>
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
    using reference = Type &;
    using const_reference = const Type &;
    using base_type = Type;

    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
        : base_type{} {}

    template<typename Args, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
    constexpr compressed_pair_element(Args &&args) noexcept(std::is_nothrow_constructible_v<base_type, Args>)
        : base_type{std::forward<Args>(args)} {}

    template<typename... Args, std::size_t... Index>
    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
        : base_type{std::forward<Args>(std::get<Index>(args))...} {}

    [[nodiscard]] constexpr reference get() noexcept {
        return *this;
    }

    [[nodiscard]] constexpr const_reference get() const noexcept {
        return *this;
    }
};

} // namespace internal

/**
 * Internal details not to be documented.
 * @endcond
 */

/**
 * @brief A compressed pair.
 *
 * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
 * reduce its final size to a minimum.
 *
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<typename First, typename Second>
class compressed_pair final
    : internal::compressed_pair_element<First, 0u>,
      internal::compressed_pair_element<Second, 1u> {
    using first_base = internal::compressed_pair_element<First, 0u>;
    using second_base = internal::compressed_pair_element<Second, 1u>;

public:
    /*! @brief The type of the first element that the pair stores. */
    using first_type = First;
    /*! @brief The type of the second element that the pair stores. */
    using second_type = Second;

    /**
     * @brief Default constructor, conditionally enabled.
     *
     * This constructor is only available when the types that the pair stores
     * are both at least default constructible.
     *
     * @tparam Dummy Dummy template parameter used for internal purposes.
     */
    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
    constexpr compressed_pair()
        : first_base{},
          second_base{} {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    constexpr compressed_pair(const compressed_pair &other) = default;

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    constexpr compressed_pair(compressed_pair &&other) = default;

    /**
     * @brief Constructs a pair from its values.
     * @tparam Arg Type of value to use to initialize the first element.
     * @tparam Other Type of value to use to initialize the second element.
     * @param arg Value to use to initialize the first element.
     * @param other Value to use to initialize the second element.
     */
    template<typename Arg, typename Other>
    constexpr compressed_pair(Arg &&arg, Other &&other)
        : first_base{std::forward<Arg>(arg)},
          second_base{std::forward<Other>(other)} {}

    /**
     * @brief Constructs a pair by forwarding the arguments to its parts.
     * @tparam Args Types of arguments to use to initialize the first element.
     * @tparam Other Types of arguments to use to initialize the second element.
     * @param args Arguments to use to initialize the first element.
     * @param other Arguments to use to initialize the second element.
     */
    template<typename... Args, typename... Other>
    constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other)
        : first_base{std::move(args), std::index_sequence_for<Args...>{}},
          second_base{std::move(other), std::index_sequence_for<Other...>{}} {}

    /**
     * @brief Copy assignment operator.
     * @param other The instance to copy from.
     * @return This compressed pair object.
     */
    constexpr compressed_pair &operator=(const compressed_pair &other) = default;

    /**
     * @brief Move assignment operator.
     * @param other The instance to move from.
     * @return This compressed pair object.
     */
    constexpr compressed_pair &operator=(compressed_pair &&other) = default;

    /**
     * @brief Returns the first element that a pair stores.
     * @return The first element that a pair stores.
     */
    [[nodiscard]] constexpr first_type &first() noexcept {
        return static_cast<first_base &>(*this).get();
    }

    /*! @copydoc first */
    [[nodiscard]] constexpr const first_type &first() const noexcept {
        return static_cast<const first_base &>(*this).get();
    }

    /**
     * @brief Returns the second element that a pair stores.
     * @return The second element that a pair stores.
     */
    [[nodiscard]] constexpr second_type &second() noexcept {
        return static_cast<second_base &>(*this).get();
    }

    /*! @copydoc second */
    [[nodiscard]] constexpr const second_type &second() const noexcept {
        return static_cast<const second_base &>(*this).get();
    }

    /**
     * @brief Swaps two compressed pair objects.
     * @param other The compressed pair to swap with.
     */
    constexpr void swap(compressed_pair &other) {
        using std::swap;
        swap(first(), other.first());
        swap(second(), other.second());
    }

    /**
     * @brief Extracts an element from the compressed pair.
     * @tparam Index An integer value that is either 0 or 1.
     * @return Returns a reference to the first element if `Index` is 0 and a
     * reference to the second element if `Index` is 1.
     */
    template<std::size_t Index>
    constexpr decltype(auto) get() noexcept {
        if constexpr(Index == 0u) {
            return first();
        } else {
            static_assert(Index == 1u, "Index out of bounds");
            return second();
        }
    }

    /*! @copydoc get */
    template<std::size_t Index>
    constexpr decltype(auto) get() const noexcept {
        if constexpr(Index == 0u) {
            return first();
        } else {
            static_assert(Index == 1u, "Index out of bounds");
            return second();
        }
    }
};

/**
 * @brief Deduction guide.
 * @tparam Type Type of value to use to initialize the first element.
 * @tparam Other Type of value to use to initialize the second element.
 */
template<typename Type, typename Other>
compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;

/**
 * @brief Swaps two compressed pair objects.
 * @tparam First The type of the first element that the pairs store.
 * @tparam Second The type of the second element that the pairs store.
 * @param lhs A valid compressed pair object.
 * @param rhs A valid compressed pair object.
 */
template<typename First, typename Second>
inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
    lhs.swap(rhs);
}

} // namespace entt

// disable structured binding support for clang 6, it messes when specializing tuple_size
#if !defined __clang_major__ || __clang_major__ > 6
namespace std {

/**
 * @brief `std::tuple_size` specialization for `compressed_pair`s.
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<typename First, typename Second>
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};

/**
 * @brief `std::tuple_element` specialization for `compressed_pair`s.
 * @tparam Index The index of the type to return.
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<size_t Index, typename First, typename Second>
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
    static_assert(Index < 2u, "Index out of bounds");
};

} // namespace std
#endif

#endif
<commit_msg>compressed pair: make the full class conditionally noexcept<commit_after>#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
#define ENTT_CORE_COMPRESSED_PAIR_HPP

#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
#include "type_traits.hpp"

namespace entt {

/**
 * @cond TURN_OFF_DOXYGEN
 * Internal details not to be documented.
 */

namespace internal {

template<typename Type, std::size_t, typename = void>
struct compressed_pair_element {
    using reference = Type &;
    using const_reference = const Type &;

    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
        : value{} {}

    template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
    constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
        : value{std::forward<Arg>(arg)} {}

    template<typename... Args, std::size_t... Index>
    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
        : value{std::forward<Args>(std::get<Index>(args))...} {}

    [[nodiscard]] constexpr reference get() noexcept {
        return value;
    }

    [[nodiscard]] constexpr const_reference get() const noexcept {
        return value;
    }

private:
    Type value;
};

template<typename Type, std::size_t Tag>
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
    using reference = Type &;
    using const_reference = const Type &;
    using base_type = Type;

    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
    constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
        : base_type{} {}

    template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
    constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
        : base_type{std::forward<Arg>(arg)} {}

    template<typename... Args, std::size_t... Index>
    constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
        : base_type{std::forward<Args>(std::get<Index>(args))...} {}

    [[nodiscard]] constexpr reference get() noexcept {
        return *this;
    }

    [[nodiscard]] constexpr const_reference get() const noexcept {
        return *this;
    }
};

} // namespace internal

/**
 * Internal details not to be documented.
 * @endcond
 */

/**
 * @brief A compressed pair.
 *
 * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
 * reduce its final size to a minimum.
 *
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<typename First, typename Second>
class compressed_pair final
    : internal::compressed_pair_element<First, 0u>,
      internal::compressed_pair_element<Second, 1u> {
    using first_base = internal::compressed_pair_element<First, 0u>;
    using second_base = internal::compressed_pair_element<Second, 1u>;

public:
    /*! @brief The type of the first element that the pair stores. */
    using first_type = First;
    /*! @brief The type of the second element that the pair stores. */
    using second_type = Second;

    /**
     * @brief Default constructor, conditionally enabled.
     *
     * This constructor is only available when the types that the pair stores
     * are both at least default constructible.
     *
     * @tparam Dummy Dummy template parameter used for internal purposes.
     */
    template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
    constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
        : first_base{},
          second_base{} {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;

    /**
     * @brief Constructs a pair from its values.
     * @tparam Arg Type of value to use to initialize the first element.
     * @tparam Other Type of value to use to initialize the second element.
     * @param arg Value to use to initialize the first element.
     * @param other Value to use to initialize the second element.
     */
    template<typename Arg, typename Other>
    constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
        : first_base{std::forward<Arg>(arg)},
          second_base{std::forward<Other>(other)} {}

    /**
     * @brief Constructs a pair by forwarding the arguments to its parts.
     * @tparam Args Types of arguments to use to initialize the first element.
     * @tparam Other Types of arguments to use to initialize the second element.
     * @param args Arguments to use to initialize the first element.
     * @param other Arguments to use to initialize the second element.
     */
    template<typename... Args, typename... Other>
    constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
        : first_base{std::move(args), std::index_sequence_for<Args...>{}},
          second_base{std::move(other), std::index_sequence_for<Other...>{}} {}

    /**
     * @brief Copy assignment operator.
     * @param other The instance to copy from.
     * @return This compressed pair object.
     */
    constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;

    /**
     * @brief Move assignment operator.
     * @param other The instance to move from.
     * @return This compressed pair object.
     */
    constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;

    /**
     * @brief Returns the first element that a pair stores.
     * @return The first element that a pair stores.
     */
    [[nodiscard]] constexpr first_type &first() noexcept {
        return static_cast<first_base &>(*this).get();
    }

    /*! @copydoc first */
    [[nodiscard]] constexpr const first_type &first() const noexcept {
        return static_cast<const first_base &>(*this).get();
    }

    /**
     * @brief Returns the second element that a pair stores.
     * @return The second element that a pair stores.
     */
    [[nodiscard]] constexpr second_type &second() noexcept {
        return static_cast<second_base &>(*this).get();
    }

    /*! @copydoc second */
    [[nodiscard]] constexpr const second_type &second() const noexcept {
        return static_cast<const second_base &>(*this).get();
    }

    /**
     * @brief Swaps two compressed pair objects.
     * @param other The compressed pair to swap with.
     */
    constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
        using std::swap;
        swap(first(), other.first());
        swap(second(), other.second());
    }

    /**
     * @brief Extracts an element from the compressed pair.
     * @tparam Index An integer value that is either 0 or 1.
     * @return Returns a reference to the first element if `Index` is 0 and a
     * reference to the second element if `Index` is 1.
     */
    template<std::size_t Index>
    constexpr decltype(auto) get() noexcept {
        if constexpr(Index == 0u) {
            return first();
        } else {
            static_assert(Index == 1u, "Index out of bounds");
            return second();
        }
    }

    /*! @copydoc get */
    template<std::size_t Index>
    constexpr decltype(auto) get() const noexcept {
        if constexpr(Index == 0u) {
            return first();
        } else {
            static_assert(Index == 1u, "Index out of bounds");
            return second();
        }
    }
};

/**
 * @brief Deduction guide.
 * @tparam Type Type of value to use to initialize the first element.
 * @tparam Other Type of value to use to initialize the second element.
 */
template<typename Type, typename Other>
compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;

/**
 * @brief Swaps two compressed pair objects.
 * @tparam First The type of the first element that the pairs store.
 * @tparam Second The type of the second element that the pairs store.
 * @param lhs A valid compressed pair object.
 * @param rhs A valid compressed pair object.
 */
template<typename First, typename Second>
inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
    lhs.swap(rhs);
}

} // namespace entt

// disable structured binding support for clang 6, it messes when specializing tuple_size
#if !defined __clang_major__ || __clang_major__ > 6
namespace std {

/**
 * @brief `std::tuple_size` specialization for `compressed_pair`s.
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<typename First, typename Second>
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};

/**
 * @brief `std::tuple_element` specialization for `compressed_pair`s.
 * @tparam Index The index of the type to return.
 * @tparam First The type of the first element that the pair stores.
 * @tparam Second The type of the second element that the pair stores.
 */
template<size_t Index, typename First, typename Second>
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
    static_assert(Index < 2u, "Index out of bounds");
};

} // namespace std
#endif

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Politecnico di Torino
 *
 * 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.
 */

#include <linux/version.h>
#include <unistd.h>
#include <string>

#include "BPF.h"
#include "catch.hpp"

TEST_CASE("test bpf table", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_table]" : "[bpf_table][!mayfail]") {
  const std::string BPF_PROGRAM = R"(
    BPF_TABLE("hash", int, int, myhash, 128);
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  std::vector<std::pair<std::string, std::string>> elements;
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());

  ebpf::BPFTable t = bpf.get_table("myhash");

  // update element
  std::string value;
  res = t.update_value("0x07", "0x42");
  REQUIRE(res.ok());
  res = t.get_value("0x7", value);
  REQUIRE(res.ok());
  REQUIRE(value == "0x42");

  // update another element
  res = t.update_value("0x11", "0x777");
  REQUIRE(res.ok());
  res = t.get_value("0x11", value);
  REQUIRE(res.ok());
  REQUIRE(value == "0x777");

  // remove value
  res = t.remove_value("0x11");
  REQUIRE(res.ok());
  res = t.get_value("0x11", value);
  REQUIRE(!res.ok());

  res = t.update_value("0x15", "0x888");
  REQUIRE(res.ok());
  res = t.get_table_offline(elements);
  REQUIRE(res.ok());
  REQUIRE(elements.size() == 2);

  // check that elements match what is in the  table
  for (auto &it : elements) {
    if (it.first == "0x15") {
      REQUIRE(it.second == "0x888");
    } else if (it.first == "0x7") {
      REQUIRE(it.second == "0x42");
    } else {
      FAIL("Element " + it.first + " should not be on the table", it.first);
    }
  }

  res = t.clear_table_non_atomic();
  REQUIRE(res.ok());
  res = t.get_table_offline(elements);
  REQUIRE(res.ok());
  REQUIRE(elements.size() == 0);

  res = t.update_value("0x07", "0x42");
  REQUIRE(!res.ok());

  res = t.get_value("0x07", value);
  REQUIRE(!res.ok());

  res = t.remove_value("0x07");
  REQUIRE(!res.ok());
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
TEST_CASE("test bpf percpu tables", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_percpu_table]" : "[bpf_percpu_table][!mayfail]") {
  const std::string BPF_PROGRAM = R"(
    BPF_PERCPU_HASH(myhash, int, u64, 128);
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());

  ebpf::BPFTable t = bpf.get_table("myhash");
  size_t ncpus = ebpf::BPFTable::get_possible_cpu_count();

  std::vector<std::string> v1(ncpus);
  for (size_t i = 0; i < ncpus; i++) {
    v1.at(i) = std::to_string(42 * i);
  }

  // update element
  std::vector<std::string> value;
  res = t.update_value("0x07", v1);
  REQUIRE(res.ok());
  res = t.get_value("0x07", value);
  REQUIRE(res.ok());
  for (size_t i = 0; i < ncpus; i++) {
    REQUIRE(42 * i == std::stoul(value.at(i), nullptr, 16));
  }
}
#endif

TEST_CASE("test bpf hash table", "[bpf_hash_table]") {
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(myhash, int, int, 128);
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());

  auto t = bpf.get_hash_table<int, int>("myhash");

  int key, value;

  // updaate element
  key = 0x08;
  value = 0x43;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  REQUIRE(t[key] == value);

  // update another element
  key = 0x12;
  value = 0x778;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  key = 0x31;
  value = 0x123;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  key = 0x12;
  value = 0;
  res = t.get_value(key, value);
  REQUIRE(res.ok());
  REQUIRE(value == 0x778);

  // remove value and dump table
  key = 0x12;
  res = t.remove_value(key);
  REQUIRE(res.ok());
  auto values = t.get_table_offline();
  REQUIRE(values.size() == 2);

  // clear table
  res = t.clear_table_non_atomic();
  REQUIRE(res.ok());
  values = t.get_table_offline();
  REQUIRE(values.size() == 0);
}

TEST_CASE("test bpf stack table", "[bpf_stack_table]") {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(id, int, int, 1);
    BPF_STACK_TRACE(stack_traces, 8);

    int on_sys_getuid(void *ctx) {
      int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
      int zero = 0, *val;
      val = id.lookup_or_try_init(&zero, &stack_id);
      if (val) {
        (*val) = stack_id;
      }

      return 0;
    }
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());
  std::string getuid_fnname = bpf.get_syscall_fnname("getuid");
  res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid");
  REQUIRE(res.ok());
  REQUIRE(getuid() >= 0);
  res = bpf.detach_kprobe(getuid_fnname);
  REQUIRE(res.ok());

  auto id = bpf.get_hash_table<int, int>("id");
  auto stack_traces = bpf.get_stack_table("stack_traces");

  int stack_id = id[0];
  REQUIRE(stack_id >= 0);

  auto addrs = stack_traces.get_stack_addr(stack_id);
  auto symbols = stack_traces.get_stack_symbol(stack_id, -1);
  REQUIRE(addrs.size() > 0);
  REQUIRE(addrs.size() == symbols.size());
  bool found = false;
  for (const auto &symbol : symbols)
    if (symbol.find("sys_getuid") != std::string::npos) {
      found = true;
      break;
    }
  REQUIRE(found);

  stack_traces.clear_table_non_atomic();
  addrs = stack_traces.get_stack_addr(stack_id);
  REQUIRE(addrs.size() == 0);
#endif
}

TEST_CASE("test bpf stack_id table", "[bpf_stack_table]") {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(id, int, int, 1);
    BPF_STACK_TRACE_BUILDID(stack_traces, 8);

    int on_sys_getuid(void *ctx) {
      int stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
      int zero = 0, *val;
      val = id.lookup_or_try_init(&zero, &stack_id);
      if (val) {
        (*val) = stack_id;
      }

      return 0;
    }
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());
  std::string getuid_fnname = bpf.get_syscall_fnname("getuid");
  res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid");
  REQUIRE(res.ok());
  REQUIRE(getuid() >= 0);
  res = bpf.detach_kprobe(getuid_fnname);
  REQUIRE(res.ok());

  auto id = bpf.get_hash_table<int, int>("id");
  auto stack_traces = bpf.get_stackbuildid_table("stack_traces");

  /* libc locations on different distributions are added below*/
  bpf.add_module("/lib/x86_64-linux-gnu/libc.so.6"); //Location of libc in ubuntu
  bpf.add_module("/lib64/libc.so.6"); //Location of libc fedora machine

  int stack_id = id[0];
  REQUIRE(stack_id >= 0);

  auto addrs = stack_traces.get_stack_addr(stack_id);
  auto symbols = stack_traces.get_stack_symbol(stack_id);
  REQUIRE(addrs.size() > 0);
  REQUIRE(addrs.size() == symbols.size());
  bool found = false;
  for (const auto &symbol : symbols) {
    if (symbol.find("getuid") != std::string::npos) {
      found = true;
      break;
    }
  }
  REQUIRE(found);

  stack_traces.clear_table_non_atomic();
  addrs = stack_traces.get_stack_addr(stack_id);
  REQUIRE(addrs.size()==0);
#endif
}
<commit_msg>test_bpf_table: Re-add deletion of 'bpf' module<commit_after>/*
 * Copyright (c) 2017 Politecnico di Torino
 *
 * 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.
 */

#include <linux/version.h>
#include <unistd.h>
#include <string>

#include "BPF.h"
#include "catch.hpp"

TEST_CASE("test bpf table", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_table]" : "[bpf_table][!mayfail]") {
  const std::string BPF_PROGRAM = R"(
    BPF_TABLE("hash", int, int, myhash, 128);
  )";

  auto bpf = std::make_unique<ebpf::BPF>();
  ebpf::StatusTuple res(0);
  std::vector<std::pair<std::string, std::string>> elements;
  res = bpf->init(BPF_PROGRAM);
  REQUIRE(res.ok());

  ebpf::BPFTable t = bpf->get_table("myhash");

  // update element
  std::string value;
  res = t.update_value("0x07", "0x42");
  REQUIRE(res.ok());
  res = t.get_value("0x7", value);
  REQUIRE(res.ok());
  REQUIRE(value == "0x42");

  // update another element
  res = t.update_value("0x11", "0x777");
  REQUIRE(res.ok());
  res = t.get_value("0x11", value);
  REQUIRE(res.ok());
  REQUIRE(value == "0x777");

  // remove value
  res = t.remove_value("0x11");
  REQUIRE(res.ok());
  res = t.get_value("0x11", value);
  REQUIRE(!res.ok());

  res = t.update_value("0x15", "0x888");
  REQUIRE(res.ok());
  res = t.get_table_offline(elements);
  REQUIRE(res.ok());
  REQUIRE(elements.size() == 2);

  // check that elements match what is in the  table
  for (auto &it : elements) {
    if (it.first == "0x15") {
      REQUIRE(it.second == "0x888");
    } else if (it.first == "0x7") {
      REQUIRE(it.second == "0x42");
    } else {
      FAIL("Element " + it.first + " should not be on the table", it.first);
    }
  }

  res = t.clear_table_non_atomic();
  REQUIRE(res.ok());
  res = t.get_table_offline(elements);
  REQUIRE(res.ok());
  REQUIRE(elements.size() == 0);

  // delete bpf_module, call to key/leaf printf/scanf must fail
  bpf.reset();

  res = t.update_value("0x07", "0x42");
  REQUIRE(!res.ok());

  res = t.get_value("0x07", value);
  REQUIRE(!res.ok());

  res = t.remove_value("0x07");
  REQUIRE(!res.ok());
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
TEST_CASE("test bpf percpu tables", ebpf::bpf_module_rw_engine_enabled() ? "[bpf_percpu_table]" : "[bpf_percpu_table][!mayfail]") {
  const std::string BPF_PROGRAM = R"(
    BPF_PERCPU_HASH(myhash, int, u64, 128);
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());

  ebpf::BPFTable t = bpf.get_table("myhash");
  size_t ncpus = ebpf::BPFTable::get_possible_cpu_count();

  std::vector<std::string> v1(ncpus);
  for (size_t i = 0; i < ncpus; i++) {
    v1.at(i) = std::to_string(42 * i);
  }

  // update element
  std::vector<std::string> value;
  res = t.update_value("0x07", v1);
  REQUIRE(res.ok());
  res = t.get_value("0x07", value);
  REQUIRE(res.ok());
  for (size_t i = 0; i < ncpus; i++) {
    REQUIRE(42 * i == std::stoul(value.at(i), nullptr, 16));
  }
}
#endif

TEST_CASE("test bpf hash table", "[bpf_hash_table]") {
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(myhash, int, int, 128);
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());

  auto t = bpf.get_hash_table<int, int>("myhash");

  int key, value;

  // updaate element
  key = 0x08;
  value = 0x43;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  REQUIRE(t[key] == value);

  // update another element
  key = 0x12;
  value = 0x778;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  key = 0x31;
  value = 0x123;
  res = t.update_value(key, value);
  REQUIRE(res.ok());
  key = 0x12;
  value = 0;
  res = t.get_value(key, value);
  REQUIRE(res.ok());
  REQUIRE(value == 0x778);

  // remove value and dump table
  key = 0x12;
  res = t.remove_value(key);
  REQUIRE(res.ok());
  auto values = t.get_table_offline();
  REQUIRE(values.size() == 2);

  // clear table
  res = t.clear_table_non_atomic();
  REQUIRE(res.ok());
  values = t.get_table_offline();
  REQUIRE(values.size() == 0);
}

TEST_CASE("test bpf stack table", "[bpf_stack_table]") {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(id, int, int, 1);
    BPF_STACK_TRACE(stack_traces, 8);

    int on_sys_getuid(void *ctx) {
      int stack_id = stack_traces.get_stackid(ctx, BPF_F_REUSE_STACKID);
      int zero = 0, *val;
      val = id.lookup_or_try_init(&zero, &stack_id);
      if (val) {
        (*val) = stack_id;
      }

      return 0;
    }
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());
  std::string getuid_fnname = bpf.get_syscall_fnname("getuid");
  res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid");
  REQUIRE(res.ok());
  REQUIRE(getuid() >= 0);
  res = bpf.detach_kprobe(getuid_fnname);
  REQUIRE(res.ok());

  auto id = bpf.get_hash_table<int, int>("id");
  auto stack_traces = bpf.get_stack_table("stack_traces");

  int stack_id = id[0];
  REQUIRE(stack_id >= 0);

  auto addrs = stack_traces.get_stack_addr(stack_id);
  auto symbols = stack_traces.get_stack_symbol(stack_id, -1);
  REQUIRE(addrs.size() > 0);
  REQUIRE(addrs.size() == symbols.size());
  bool found = false;
  for (const auto &symbol : symbols)
    if (symbol.find("sys_getuid") != std::string::npos) {
      found = true;
      break;
    }
  REQUIRE(found);

  stack_traces.clear_table_non_atomic();
  addrs = stack_traces.get_stack_addr(stack_id);
  REQUIRE(addrs.size() == 0);
#endif
}

TEST_CASE("test bpf stack_id table", "[bpf_stack_table]") {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
  const std::string BPF_PROGRAM = R"(
    BPF_HASH(id, int, int, 1);
    BPF_STACK_TRACE_BUILDID(stack_traces, 8);

    int on_sys_getuid(void *ctx) {
      int stack_id = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
      int zero = 0, *val;
      val = id.lookup_or_try_init(&zero, &stack_id);
      if (val) {
        (*val) = stack_id;
      }

      return 0;
    }
  )";

  ebpf::BPF bpf;
  ebpf::StatusTuple res(0);
  res = bpf.init(BPF_PROGRAM);
  REQUIRE(res.ok());
  std::string getuid_fnname = bpf.get_syscall_fnname("getuid");
  res = bpf.attach_kprobe(getuid_fnname, "on_sys_getuid");
  REQUIRE(res.ok());
  REQUIRE(getuid() >= 0);
  res = bpf.detach_kprobe(getuid_fnname);
  REQUIRE(res.ok());

  auto id = bpf.get_hash_table<int, int>("id");
  auto stack_traces = bpf.get_stackbuildid_table("stack_traces");

  /* libc locations on different distributions are added below*/
  bpf.add_module("/lib/x86_64-linux-gnu/libc.so.6"); //Location of libc in ubuntu
  bpf.add_module("/lib64/libc.so.6"); //Location of libc fedora machine

  int stack_id = id[0];
  REQUIRE(stack_id >= 0);

  auto addrs = stack_traces.get_stack_addr(stack_id);
  auto symbols = stack_traces.get_stack_symbol(stack_id);
  REQUIRE(addrs.size() > 0);
  REQUIRE(addrs.size() == symbols.size());
  bool found = false;
  for (const auto &symbol : symbols) {
    if (symbol.find("getuid") != std::string::npos) {
      found = true;
      break;
    }
  }
  REQUIRE(found);

  stack_traces.clear_table_non_atomic();
  addrs = stack_traces.get_stack_addr(stack_id);
  REQUIRE(addrs.size()==0);
#endif
}
<|endoftext|>


<commit_before>/******************************************************************************/
/*                                                                            */
/*                 X r d S e c P r o t o c o l s s l . h h                    */
/*                                                                            */
/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University  */
/*                            All Rights Reserved                             */
/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
/*              DE-AC02-76-SFO0515 with the Department of Energy              */
/******************************************************************************/

//       $Id$

#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <iostream>
#include <stdlib.h>
#include <strings.h>
#include <grp.h>
#include <pwd.h>

#define OPENSSL_THREAD_DEFINES
#include <openssl/opensslconf.h>

#include <openssl/crypto.h>
#include <openssl/x509v3.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>

#include "XrdNet/XrdNetDNS.hh"
#include "XrdOuc/XrdOucErrInfo.hh"
#include "XrdOuc/XrdOucHash.hh"
#include "XrdOuc/XrdOucString.hh"
#include "XrdOuc/XrdOucTrace.hh"
#include "XrdOuc/XrdOucTokenizer.hh"
#include "XrdSys/XrdSysPthread.hh"
#include "XrdSys/XrdSysLogger.hh"
#include "XrdSec/XrdSecInterface.hh"
#include "XrdSec/XrdSecTLayer.hh"
#include "XrdSecssl/XrdSecProtocolsslTrace.hh"
#include "XrdSecssl/XrdSecProtocolsslProc.hh"
#include "libsslGridSite/grst_verifycallback.h"
#include "libsslGridSite/gridsite.h"

#define EXPORTKEYSTRENGTH 10

#define PROTOCOLSSL_MAX_CRYPTO_MUTEX 256

// fix for SSL 098 stuff and g++

#ifdef R__SSL_GE_098
#undef PEM_read_SSL_SESSION
#undef PEM_write_SSL_SESSION

#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (void *(*)(void **, const unsigned char **, long int))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(void **)x,cb,u)

#define PEM_write_SSL_SESSION(fp,x) PEM_ASN1_write((int (*)(void*, unsigned char**))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,fp, (char *)x,  NULL,NULL,0,NULL,NULL)
#else
#ifdef __macos__
#undef PEM_read_SSL_SESSION
#undef PEM_write_SSL_SESSION

#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (char *\
(*)(...))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(char **)x  ,cb,u)
#endif
#endif

#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
                         *((c)++)=(unsigned char)(((l)>>16)&0xff), \
                         *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
                         *((c)++)=(unsigned char)(((l)    )&0xff))

#ifdef SUNCC
#define __FUNCTION__ "-unknown-"
#endif

static XrdOucTrace        *SSLxTrace=0;

class XrdSecProtocolssl;

#define MAX_SESSION_ID_ATTEMPTS 10

/******************************************************************************/
/*              X r d S e c P r o t o c o l s s l C l a s s                   */
/******************************************************************************/

class XrdSecsslSessionLock {
private:
static  XrdSysMutex sessionmutex;
  int sessionfd;

public:
  XrdSecsslSessionLock() {sessionfd=0;}
  bool SoftLock() { sessionmutex.Lock();return true;}
  bool SoftUnLock() {sessionmutex.UnLock();return true;}
#ifdef SUNCC
  bool HardLock(const char* path) {return true;}
  bool HardUnLock() {return true;}
  ~XrdSecsslSessionLock() {sessionmutex.UnLock();}
#else
  bool HardLock(const char* path) {sessionfd = open(path,O_RDWR); if ( (sessionfd>0) && (!flock(sessionfd,LOCK_EX)))return true;return false;}
  bool HardUnLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);sessionfd=0;}return true;}
  ~XrdSecsslSessionLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);};}
#endif

};

class XrdSecProtocolssl : public XrdSecTLayer
{
public:
  friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor

  XrdSecProtocolssl(const char* hostname, const struct sockaddr  *ipaddr) : XrdSecTLayer("ssl",XrdSecTLayer::isClient) {
    credBuff    = 0;
    ssl         = 0;
    Entity.name = 0;
    Entity.grps = 0;
    Entity.endorsements = 0;
    host        = hostname;
    if (ipaddr)
      Entity.host = (XrdNetDNS::getHostName((sockaddr&)*ipaddr));
    else
      Entity.host = strdup("");
    proxyBuff[0]=0;
    client_cert=0;
    server_cert=0;
    ssl = 0 ;
    clientctx = 0;
    terminate = 0;
  }

  virtual void   secClient(int theFD, XrdOucErrInfo      *einfo);
  virtual void   secServer(int theFD, XrdOucErrInfo      *einfo=0);

  // triggers purging of expired SecTLayer threads
  static  int    dummy(const char* key, XrdSecProtocolssl *ssl, void* Arg) { return 0;}

  // delayed garbage collection
  virtual void              Delete() { terminate = true; if (secTid) XrdSysThread::Join(secTid,NULL); secTid=0; delete this; }

  static int GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len);
  static int NewSession(SSL* ssl, SSL_SESSION *pNew);
  static int GetSession(SSL* ssl, SSL_SESSION *pNew);

  static char*              SessionIdContext ;
  static char*              sslcadir;
  static char*              sslvomsdir;
  static char*              sslserverkeyfile;
  static char*              sslkeyfile;
  static char*              sslcertfile;
  static char*              sslproxyexportdir;
  static bool               sslproxyexportplain;
  static char               sslserverexportpassword[EXPORTKEYSTRENGTH+1];
  static int                threadsinuse;
  static char*              gridmapfile;
  static char*              vomsmapfile;
  static bool               mapuser;
  static bool               mapnobody;
  static bool               mapgroup;
  static bool               mapcerncertificates;
  static int                debug;
  static time_t             sslsessionlifetime;
  static int                sslselecttimeout;
  static int                sslsessioncachesize;
  static char*              procdir;
  static XrdSecProtocolsslProc* proc;

  static int                errortimeout;
  static int                errorverify;
  static int                errorqueue;
  static int                erroraccept;
  static int                errorabort;
  static int                errorread;
  static int                forwardedproxies;

  static bool               isServer;
  static bool               forwardProxy;
  static bool               allowSessions;
  static X509_STORE*        store;
  static X509_LOOKUP*       lookup;
  static int                verifydepth;
  static int                verifyindex;
  int                       sessionfd;
  X509*    client_cert;
  X509*    server_cert;
  XrdOucString              host;

  // User/Group mapping
  static void ReloadGridMapFile();
  static void ReloadVomsMapFile();
  static bool VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup);

  static void GetEnvironment();
  static  XrdOucHash<XrdOucString>  gridmapstore;
  static  XrdOucHash<XrdOucString>  vomsmapstore;
  static  XrdOucHash<XrdOucString>  stringstore;
  static  XrdSysMutex               StoreMutex;
  static  XrdSysMutex               VomsMapMutex;
  static  XrdSysMutex               GridMapMutex;
  static  XrdSysMutex*              CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX];
  static  XrdSysMutex               ThreadsInUseMutex;
  static  XrdSysMutex               ErrorMutex;

  // for error logging and tracing
  static XrdSysLogger       Logger;
  static XrdSysError        ssleDest;
  static time_t             storeLoadTime;

  typedef struct {
    int verbose_mode;
    int verify_depth;
    int always_continue;
  } sslverify_t;

  char proxyBuff[16384];
  static SSL_CTX* ctx;
  SSL_CTX* clientctx;

  XrdSysMutex SSLMutex;
  bool terminate;
  ~XrdSecProtocolssl() {
    if (credBuff)    free(credBuff);
    if (Entity.name) free(Entity.name);
    if (Entity.grps) free(Entity.grps);
    if (Entity.role) free(Entity.role);
    if (Entity.host) free(Entity.host);
    SSLMutex.Lock();
    if (ssl) SSL_free(ssl);ssl=0;
    if (client_cert) X509_free(client_cert);
    if (server_cert) X509_free(server_cert);
    SSLMutex.UnLock();
  }

  static int Fatal(XrdOucErrInfo *erp, const char* msg, int rc);

  struct sockaddr           hostaddr;      // Client-side only
  char                     *credBuff;      // Credentials buffer (server)
  int                       Step;          // Indicates step in authentication

  int sd;
  int listen_sd;
  struct sockaddr_in sa_serv;
  struct sockaddr_in sa_cli;
  SSL*     ssl;
};

extern "C"
{
  char  *XrdSecProtocolsslInit(const char     mode,
			       const char    *parms,
			       XrdOucErrInfo *erp);
}

class XrdSecsslThreadInUse {
public:
  XrdSecsslThreadInUse() {XrdSecProtocolssl::ThreadsInUseMutex.Lock();XrdSecProtocolssl::threadsinuse++;XrdSecProtocolssl::ThreadsInUseMutex.UnLock();}
  ~XrdSecsslThreadInUse() {XrdSecProtocolssl::ThreadsInUseMutex.Lock();XrdSecProtocolssl::threadsinuse--;XrdSecProtocolssl::ThreadsInUseMutex.UnLock();}
};

<commit_msg>From A.Peters: possible fix the compilation problem on MacOsX Leopard<commit_after>/******************************************************************************/
/*                                                                            */
/*                 X r d S e c P r o t o c o l s s l . h h                    */
/*                                                                            */
/* (c) 2007 by the Board of Trustees of the Leland Stanford, Jr., University  */
/*                            All Rights Reserved                             */
/*   Produced by Andrew Hanushevsky for Stanford University under contract    */
/*              DE-AC02-76-SFO0515 with the Department of Energy              */
/******************************************************************************/

//       $Id$

#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <iostream>
#include <stdlib.h>
#include <strings.h>
#include <grp.h>
#include <pwd.h>

#define OPENSSL_THREAD_DEFINES
#include <openssl/opensslconf.h>

#include <openssl/crypto.h>
#include <openssl/x509v3.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>

#include "XrdNet/XrdNetDNS.hh"
#include "XrdOuc/XrdOucErrInfo.hh"
#include "XrdOuc/XrdOucHash.hh"
#include "XrdOuc/XrdOucString.hh"
#include "XrdOuc/XrdOucTrace.hh"
#include "XrdOuc/XrdOucTokenizer.hh"
#include "XrdSys/XrdSysPthread.hh"
#include "XrdSys/XrdSysLogger.hh"
#include "XrdSec/XrdSecInterface.hh"
#include "XrdSec/XrdSecTLayer.hh"
#include "XrdSecssl/XrdSecProtocolsslTrace.hh"
#include "XrdSecssl/XrdSecProtocolsslProc.hh"
#include "libsslGridSite/grst_verifycallback.h"
#include "libsslGridSite/gridsite.h"

#define EXPORTKEYSTRENGTH 10

#define PROTOCOLSSL_MAX_CRYPTO_MUTEX 256

// fix for SSL 098 stuff and g++

#ifdef R__SSL_GE_098
#undef PEM_read_SSL_SESSION
#undef PEM_write_SSL_SESSION

#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (void *(*)(void **, const unsigned char **, long int))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(void **)x,cb,u)

#define PEM_write_SSL_SESSION(fp,x) PEM_ASN1_write((int (*)(void*, unsigned char**))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,fp, (char *)x,  NULL,NULL,0,NULL,NULL)
#else
#ifdef __macos__
#undef PEM_read_SSL_SESSION
#undef PEM_write_SSL_SESSION

#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( (char *(*)(...))d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(char **)x  ,cb,u)
#define PEM_write_SSL_SESSION(fp,x) PEM_ASN1_write((int(*)(...))i2d_SSL_SESSION, PEM_STRING_SSL_SESSION,fp, (char *)x,NULL,NULL,0,NULL,NULL)
#endif
#endif

#define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
                         *((c)++)=(unsigned char)(((l)>>16)&0xff), \
                         *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
                         *((c)++)=(unsigned char)(((l)    )&0xff))

#ifdef SUNCC
#define __FUNCTION__ "-unknown-"
#endif

static XrdOucTrace        *SSLxTrace=0;

class XrdSecProtocolssl;

#define MAX_SESSION_ID_ATTEMPTS 10

/******************************************************************************/
/*              X r d S e c P r o t o c o l s s l C l a s s                   */
/******************************************************************************/

class XrdSecsslSessionLock {
private:
static  XrdSysMutex sessionmutex;
  int sessionfd;

public:
  XrdSecsslSessionLock() {sessionfd=0;}
  bool SoftLock() { sessionmutex.Lock();return true;}
  bool SoftUnLock() {sessionmutex.UnLock();return true;}
#ifdef SUNCC
  bool HardLock(const char* path) {return true;}
  bool HardUnLock() {return true;}
  ~XrdSecsslSessionLock() {sessionmutex.UnLock();}
#else
  bool HardLock(const char* path) {sessionfd = open(path,O_RDWR); if ( (sessionfd>0) && (!flock(sessionfd,LOCK_EX)))return true;return false;}
  bool HardUnLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);sessionfd=0;}return true;}
  ~XrdSecsslSessionLock() {if (sessionfd>0) {flock(sessionfd,LOCK_UN);close(sessionfd);};}
#endif

};

class XrdSecProtocolssl : public XrdSecTLayer
{
public:
  friend class XrdSecProtocolDummy; // Avoid stupid gcc warnings about destructor

  XrdSecProtocolssl(const char* hostname, const struct sockaddr  *ipaddr) : XrdSecTLayer("ssl",XrdSecTLayer::isClient) {
    credBuff    = 0;
    ssl         = 0;
    Entity.name = 0;
    Entity.grps = 0;
    Entity.endorsements = 0;
    host        = hostname;
    if (ipaddr)
      Entity.host = (XrdNetDNS::getHostName((sockaddr&)*ipaddr));
    else
      Entity.host = strdup("");
    proxyBuff[0]=0;
    client_cert=0;
    server_cert=0;
    ssl = 0 ;
    clientctx = 0;
    terminate = 0;
  }

  virtual void   secClient(int theFD, XrdOucErrInfo      *einfo);
  virtual void   secServer(int theFD, XrdOucErrInfo      *einfo=0);

  // triggers purging of expired SecTLayer threads
  static  int    dummy(const char* key, XrdSecProtocolssl *ssl, void* Arg) { return 0;}

  // delayed garbage collection
  virtual void              Delete() { terminate = true; if (secTid) XrdSysThread::Join(secTid,NULL); secTid=0; delete this; }

  static int GenerateSession(const SSL* ssl, unsigned char *id, unsigned int *id_len);
  static int NewSession(SSL* ssl, SSL_SESSION *pNew);
  static int GetSession(SSL* ssl, SSL_SESSION *pNew);

  static char*              SessionIdContext ;
  static char*              sslcadir;
  static char*              sslvomsdir;
  static char*              sslserverkeyfile;
  static char*              sslkeyfile;
  static char*              sslcertfile;
  static char*              sslproxyexportdir;
  static bool               sslproxyexportplain;
  static char               sslserverexportpassword[EXPORTKEYSTRENGTH+1];
  static int                threadsinuse;
  static char*              gridmapfile;
  static char*              vomsmapfile;
  static bool               mapuser;
  static bool               mapnobody;
  static bool               mapgroup;
  static bool               mapcerncertificates;
  static int                debug;
  static time_t             sslsessionlifetime;
  static int                sslselecttimeout;
  static int                sslsessioncachesize;
  static char*              procdir;
  static XrdSecProtocolsslProc* proc;

  static int                errortimeout;
  static int                errorverify;
  static int                errorqueue;
  static int                erroraccept;
  static int                errorabort;
  static int                errorread;
  static int                forwardedproxies;

  static bool               isServer;
  static bool               forwardProxy;
  static bool               allowSessions;
  static X509_STORE*        store;
  static X509_LOOKUP*       lookup;
  static int                verifydepth;
  static int                verifyindex;
  int                       sessionfd;
  X509*    client_cert;
  X509*    server_cert;
  XrdOucString              host;

  // User/Group mapping
  static void ReloadGridMapFile();
  static void ReloadVomsMapFile();
  static bool VomsMapGroups(const char* groups, XrdOucString& allgroups, XrdOucString& defaultgroup);

  static void GetEnvironment();
  static  XrdOucHash<XrdOucString>  gridmapstore;
  static  XrdOucHash<XrdOucString>  vomsmapstore;
  static  XrdOucHash<XrdOucString>  stringstore;
  static  XrdSysMutex               StoreMutex;
  static  XrdSysMutex               VomsMapMutex;
  static  XrdSysMutex               GridMapMutex;
  static  XrdSysMutex*              CryptoMutexPool[PROTOCOLSSL_MAX_CRYPTO_MUTEX];
  static  XrdSysMutex               ThreadsInUseMutex;
  static  XrdSysMutex               ErrorMutex;

  // for error logging and tracing
  static XrdSysLogger       Logger;
  static XrdSysError        ssleDest;
  static time_t             storeLoadTime;

  typedef struct {
    int verbose_mode;
    int verify_depth;
    int always_continue;
  } sslverify_t;

  char proxyBuff[16384];
  static SSL_CTX* ctx;
  SSL_CTX* clientctx;

  XrdSysMutex SSLMutex;
  bool terminate;
  ~XrdSecProtocolssl() {
    if (credBuff)    free(credBuff);
    if (Entity.name) free(Entity.name);
    if (Entity.grps) free(Entity.grps);
    if (Entity.role) free(Entity.role);
    if (Entity.host) free(Entity.host);
    SSLMutex.Lock();
    if (ssl) SSL_free(ssl);ssl=0;
    if (client_cert) X509_free(client_cert);
    if (server_cert) X509_free(server_cert);
    SSLMutex.UnLock();
  }

  static int Fatal(XrdOucErrInfo *erp, const char* msg, int rc);

  struct sockaddr           hostaddr;      // Client-side only
  char                     *credBuff;      // Credentials buffer (server)
  int                       Step;          // Indicates step in authentication

  int sd;
  int listen_sd;
  struct sockaddr_in sa_serv;
  struct sockaddr_in sa_cli;
  SSL*     ssl;
};

extern "C"
{
  char  *XrdSecProtocolsslInit(const char     mode,
			       const char    *parms,
			       XrdOucErrInfo *erp);
}

class XrdSecsslThreadInUse {
public:
  XrdSecsslThreadInUse() {XrdSecProtocolssl::ThreadsInUseMutex.Lock();XrdSecProtocolssl::threadsinuse++;XrdSecProtocolssl::ThreadsInUseMutex.UnLock();}
  ~XrdSecsslThreadInUse() {XrdSecProtocolssl::ThreadsInUseMutex.Lock();XrdSecProtocolssl::threadsinuse--;XrdSecProtocolssl::ThreadsInUseMutex.UnLock();}
};

<|endoftext|>


<commit_before>#include "config.h"
#include <gtest/gtest.h>
#include <libcouchbase/couchbase.h>
#include "mock-environment.h"
#include "internal.h"
#include "bucketconfig/clconfig.h"
#include <set>

class Confmon : public ::testing::Test
{
    void SetUp() {
        MockEnvironment::Reset();
    }
};

struct evstop_listener {
    clconfig_listener base;
    lcb_io_opt_t io;
    int called;
};

extern "C" {
static void listen_callback1(clconfig_listener *lsn, clconfig_event_t event,
                             clconfig_info *info)
{
    if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) {
        return;
    }

    evstop_listener *me = reinterpret_cast<evstop_listener*>(lsn);
    me->called = 1;
    me->io->v.v0.stop_event_loop(me->io);
}
}

TEST_F(Confmon, testBasic)
{
    HandleWrap hw;
    lcb_t instance;
    MockEnvironment::getInstance()->createConnection(hw, instance);

    lcb_confmon *mon = lcb_confmon_create(&instance->settings);
    lcb_confmon_set_nodes(mon, instance->usernodes, NULL);
    clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);
    lcb_clconfig_http_enable(http);
    lcb_clconfig_http_set_nodes(http, instance->usernodes);

    lcb_confmon_prepare(mon);

    EXPECT_EQ(NULL, lcb_confmon_get_config(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon));

    // Try to find a provider..
    clconfig_provider *provider = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);
    ASSERT_NE(0, provider->enabled);

    struct evstop_listener listener;
    memset(&listener, 0, sizeof(listener));

    listener.base.callback = listen_callback1;
    listener.base.parent = mon;
    listener.io = hw.getIo();

    lcb_confmon_add_listener(mon, &listener.base);
    lcb_confmon_start(mon);
    hw.getIo()->v.v0.run_event_loop(hw.getIo());

    ASSERT_NE(0, listener.called);

    lcb_confmon_destroy(mon);
}

struct listener2 {
    clconfig_listener base;
    int call_count;
    lcb_io_opt_t io;
    clconfig_method_t last_source;
    std::set<clconfig_event_t> expected_events;

    void reset() {
        call_count = 0;
        last_source = LCB_CLCONFIG_PHONY;
        expected_events.clear();
    }
};

static struct listener2* getListener2(const void *p)
{
    return reinterpret_cast<struct listener2*>(const_cast<void*>(p));
}

extern "C" {
static void listen_callback2(clconfig_listener *prov,
                             clconfig_event_t event,
                             clconfig_info *info)
{
    // Increase the number of times we've received a callback..
    struct listener2* lsn = getListener2(prov);

    if (event == CLCONFIG_EVENT_MONITOR_STOPPED) {
        lsn->io->v.v0.stop_event_loop(lsn->io);
        return;
    }

    if (!lsn->expected_events.empty()) {
        if (lsn->expected_events.end() ==
            lsn->expected_events.find(event)) {
            return;
        }
    }

    lsn->call_count++;
    lsn->last_source = info->origin;
    lsn->io->v.v0.stop_event_loop(lsn->io);
}
}

static void runConfmon(lcb_io_opt_t io, lcb_confmon *mon)
{
    io->v.v0.run_event_loop(io);
}

TEST_F(Confmon, testCycle)
{
    HandleWrap hw;
    lcb_t instance;
    lcb_create_st cropts;
    MockEnvironment *mock = MockEnvironment::getInstance();

    if (mock->isRealCluster()) {
        return;
    }

    mock->createConnection(hw, instance);
    instance->settings.bc_http_stream_time = 100000;
    instance->memd_sockpool->idle_timeout = 100000;

    lcb_confmon *mon = lcb_confmon_create(&instance->settings);
    lcb_confmon_set_nodes(mon, instance->usernodes, NULL);

    struct listener2 lsn;
    memset(&lsn, 0, sizeof(lsn));
    lsn.base.callback = listen_callback2;
    lsn.io = hw.getIo();
    lsn.reset();

    lcb_confmon_add_listener(mon, &lsn.base);

    mock->makeConnectParams(cropts, NULL);
    clconfig_provider *cccp = lcb_confmon_get_provider(mon, LCB_CLCONFIG_CCCP);
    clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);

    hostlist_t hl = hostlist_create();
    hostlist_add_stringz(hl, cropts.v.v2.mchosts, 11210);
    lcb_clconfig_cccp_enable(cccp, instance);
    lcb_clconfig_cccp_set_nodes(cccp, hl);

    lcb_clconfig_http_enable(http);
    lcb_clconfig_http_set_nodes(http, instance->usernodes);
    hostlist_destroy(hl);

    lcb_confmon_prepare(mon);
    lcb_confmon_start(mon);

    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG);
    runConfmon(lsn.io, mon);

    // Ensure CCCP is functioning properly and we're called only once.
    ASSERT_EQ(1, lsn.call_count);
    ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source);

    lcb_confmon_start(mon);
    lsn.reset();
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG);
    runConfmon(lsn.io, mon);
    ASSERT_EQ(1, lsn.call_count);
    ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source);

    mock->setCCCP(false);
    mock->failoverNode(5);
    lsn.reset();
    lcb_confmon_start(mon);
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG);
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG);
    runConfmon(lsn.io, mon);
    ASSERT_EQ(LCB_CLCONFIG_HTTP, lsn.last_source);
    ASSERT_EQ(1, lsn.call_count);
    lcb_confmon_destroy(mon);
}
<commit_msg>confmon test: don't memset listener<commit_after>#include "config.h"
#include <gtest/gtest.h>
#include <libcouchbase/couchbase.h>
#include "mock-environment.h"
#include "internal.h"
#include "bucketconfig/clconfig.h"
#include <set>

class Confmon : public ::testing::Test
{
    void SetUp() {
        MockEnvironment::Reset();
    }
};

struct evstop_listener {
    clconfig_listener base;
    lcb_io_opt_t io;
    int called;
};

extern "C" {
static void listen_callback1(clconfig_listener *lsn, clconfig_event_t event,
                             clconfig_info *info)
{
    if (event != CLCONFIG_EVENT_GOT_NEW_CONFIG) {
        return;
    }

    evstop_listener *me = reinterpret_cast<evstop_listener*>(lsn);
    me->called = 1;
    me->io->v.v0.stop_event_loop(me->io);
}
}

TEST_F(Confmon, testBasic)
{
    HandleWrap hw;
    lcb_t instance;
    MockEnvironment::getInstance()->createConnection(hw, instance);

    lcb_confmon *mon = lcb_confmon_create(&instance->settings);
    lcb_confmon_set_nodes(mon, instance->usernodes, NULL);
    clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);
    lcb_clconfig_http_enable(http);
    lcb_clconfig_http_set_nodes(http, instance->usernodes);

    lcb_confmon_prepare(mon);

    EXPECT_EQ(NULL, lcb_confmon_get_config(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_start(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon));
    EXPECT_EQ(LCB_SUCCESS, lcb_confmon_stop(mon));

    // Try to find a provider..
    clconfig_provider *provider = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);
    ASSERT_NE(0, provider->enabled);

    struct evstop_listener listener;
    memset(&listener, 0, sizeof(listener));

    listener.base.callback = listen_callback1;
    listener.base.parent = mon;
    listener.io = hw.getIo();

    lcb_confmon_add_listener(mon, &listener.base);
    lcb_confmon_start(mon);
    hw.getIo()->v.v0.run_event_loop(hw.getIo());

    ASSERT_NE(0, listener.called);

    lcb_confmon_destroy(mon);
}

struct listener2 {
    clconfig_listener base;
    int call_count;
    lcb_io_opt_t io;
    clconfig_method_t last_source;
    std::set<clconfig_event_t> expected_events;

    void reset() {
        call_count = 0;
        last_source = LCB_CLCONFIG_PHONY;
        expected_events.clear();
    }

    listener2() {
        memset(&base, 0, sizeof(base));
        io = NULL;
        reset();
    }
};

static struct listener2* getListener2(const void *p)
{
    return reinterpret_cast<struct listener2*>(const_cast<void*>(p));
}

extern "C" {
static void listen_callback2(clconfig_listener *prov,
                             clconfig_event_t event,
                             clconfig_info *info)
{
    // Increase the number of times we've received a callback..
    struct listener2* lsn = getListener2(prov);

    if (event == CLCONFIG_EVENT_MONITOR_STOPPED) {
        lsn->io->v.v0.stop_event_loop(lsn->io);
        return;
    }

    if (!lsn->expected_events.empty()) {
        if (lsn->expected_events.end() ==
            lsn->expected_events.find(event)) {
            return;
        }
    }

    lsn->call_count++;
    lsn->last_source = info->origin;
    lsn->io->v.v0.stop_event_loop(lsn->io);
}
}

static void runConfmon(lcb_io_opt_t io, lcb_confmon *mon)
{
    io->v.v0.run_event_loop(io);
}

TEST_F(Confmon, testCycle)
{
    HandleWrap hw;
    lcb_t instance;
    lcb_create_st cropts;
    MockEnvironment *mock = MockEnvironment::getInstance();

    if (mock->isRealCluster()) {
        return;
    }

    mock->createConnection(hw, instance);
    instance->settings.bc_http_stream_time = 100000;
    instance->memd_sockpool->idle_timeout = 100000;

    lcb_confmon *mon = lcb_confmon_create(&instance->settings);
    lcb_confmon_set_nodes(mon, instance->usernodes, NULL);

    struct listener2 lsn;
    lsn.base.callback = listen_callback2;
    lsn.io = hw.getIo();
    lsn.reset();

    lcb_confmon_add_listener(mon, &lsn.base);

    mock->makeConnectParams(cropts, NULL);
    clconfig_provider *cccp = lcb_confmon_get_provider(mon, LCB_CLCONFIG_CCCP);
    clconfig_provider *http = lcb_confmon_get_provider(mon, LCB_CLCONFIG_HTTP);

    hostlist_t hl = hostlist_create();
    hostlist_add_stringz(hl, cropts.v.v2.mchosts, 11210);
    lcb_clconfig_cccp_enable(cccp, instance);
    lcb_clconfig_cccp_set_nodes(cccp, hl);

    lcb_clconfig_http_enable(http);
    lcb_clconfig_http_set_nodes(http, instance->usernodes);
    hostlist_destroy(hl);

    lcb_confmon_prepare(mon);
    lcb_confmon_start(mon);

    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG);
    runConfmon(lsn.io, mon);

    // Ensure CCCP is functioning properly and we're called only once.
    ASSERT_EQ(1, lsn.call_count);
    ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source);

    lcb_confmon_start(mon);
    lsn.reset();
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG);
    runConfmon(lsn.io, mon);
    ASSERT_EQ(1, lsn.call_count);
    ASSERT_EQ(LCB_CLCONFIG_CCCP, lsn.last_source);

    mock->setCCCP(false);
    mock->failoverNode(5);
    lsn.reset();
    lcb_confmon_start(mon);
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_ANY_CONFIG);
    lsn.expected_events.insert(CLCONFIG_EVENT_GOT_NEW_CONFIG);
    runConfmon(lsn.io, mon);
    ASSERT_EQ(LCB_CLCONFIG_HTTP, lsn.last_source);
    ASSERT_EQ(1, lsn.call_count);
    lcb_confmon_destroy(mon);
}
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// executor_context.cpp
//
// Identification: src/executor/executor_context.cpp
//
// Copyright (c) 2015-2018, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "executor/executor_context.h"

#include "storage/storage_manager.h"

namespace peloton {
namespace executor {

////////////////////////////////////////////////////////////////////////////////
///
/// ExecutorContext
///
////////////////////////////////////////////////////////////////////////////////

ExecutorContext::ExecutorContext(concurrency::TransactionContext *transaction,
                                 codegen::QueryParameters parameters)
    : transaction_(transaction),
      parameters_(std::move(parameters)),
      storage_manager_(storage::StorageManager::GetInstance()),
      thread_states_(pool_) {}

concurrency::TransactionContext *ExecutorContext::GetTransaction() const {
  return transaction_;
}

const std::vector<type::Value> &ExecutorContext::GetParamValues() const {
  return parameters_.GetParameterValues();
}

storage::StorageManager &ExecutorContext::GetStorageManager() const {
  return *storage_manager_;
}

codegen::QueryParameters &ExecutorContext::GetParams() { return parameters_; }

type::EphemeralPool *ExecutorContext::GetPool() { return &pool_; }

ExecutorContext::ThreadStates &ExecutorContext::GetThreadStates() {
  return thread_states_;
}

////////////////////////////////////////////////////////////////////////////////
///
/// ThreadStates
///
////////////////////////////////////////////////////////////////////////////////

ExecutorContext::ThreadStates::ThreadStates(type::EphemeralPool &pool)
    : pool_(pool), num_threads_(0), state_size_(0), states_(nullptr) {}

void ExecutorContext::ThreadStates::Reset(const uint32_t state_size) {
  if (states_ != nullptr) {
    pool_.Free(states_);
    states_ = nullptr;
  }
  num_threads_ = 0;
  // Always fill out to nearest cache-line to prevent false sharing of states
  // between different threads.
  uint32_t pad = state_size & ~CACHELINE_SIZE;
  state_size_ = state_size + (pad != 0 ? CACHELINE_SIZE - pad : pad);
}

void ExecutorContext::ThreadStates::Allocate(const uint32_t num_threads) {
  PL_ASSERT(state_size_ > 0);
  PL_ASSERT(states_ == nullptr);
  num_threads_ = num_threads;
  uint32_t alloc_size = num_threads_ * state_size_;
  states_ = reinterpret_cast<char *>(pool_.Allocate(alloc_size));
}

char *ExecutorContext::ThreadStates::AccessThreadState(
    const uint32_t thread_id) const {
  PL_ASSERT(state_size_ > 0);
  PL_ASSERT(states_ != nullptr);
  PL_ASSERT(thread_id < num_threads_);
  return states_ + (thread_id * state_size_);
}

}  // namespace executor
}  // namespace peloton
<commit_msg>Clear memory for state space after allocation<commit_after>//===----------------------------------------------------------------------===//
//
//                         Peloton
//
// executor_context.cpp
//
// Identification: src/executor/executor_context.cpp
//
// Copyright (c) 2015-2018, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "executor/executor_context.h"

#include "storage/storage_manager.h"

namespace peloton {
namespace executor {

////////////////////////////////////////////////////////////////////////////////
///
/// ExecutorContext
///
////////////////////////////////////////////////////////////////////////////////

ExecutorContext::ExecutorContext(concurrency::TransactionContext *transaction,
                                 codegen::QueryParameters parameters)
    : transaction_(transaction),
      parameters_(std::move(parameters)),
      storage_manager_(storage::StorageManager::GetInstance()),
      thread_states_(pool_) {}

concurrency::TransactionContext *ExecutorContext::GetTransaction() const {
  return transaction_;
}

const std::vector<type::Value> &ExecutorContext::GetParamValues() const {
  return parameters_.GetParameterValues();
}

storage::StorageManager &ExecutorContext::GetStorageManager() const {
  return *storage_manager_;
}

codegen::QueryParameters &ExecutorContext::GetParams() { return parameters_; }

type::EphemeralPool *ExecutorContext::GetPool() { return &pool_; }

ExecutorContext::ThreadStates &ExecutorContext::GetThreadStates() {
  return thread_states_;
}

////////////////////////////////////////////////////////////////////////////////
///
/// ThreadStates
///
////////////////////////////////////////////////////////////////////////////////

ExecutorContext::ThreadStates::ThreadStates(type::EphemeralPool &pool)
    : pool_(pool), num_threads_(0), state_size_(0), states_(nullptr) {}

void ExecutorContext::ThreadStates::Reset(const uint32_t state_size) {
  if (states_ != nullptr) {
    pool_.Free(states_);
    states_ = nullptr;
  }
  num_threads_ = 0;
  // Always fill out to nearest cache-line to prevent false sharing of states
  // between different threads.
  uint32_t pad = state_size & ~CACHELINE_SIZE;
  state_size_ = state_size + (pad != 0 ? CACHELINE_SIZE - pad : pad);
}

void ExecutorContext::ThreadStates::Allocate(const uint32_t num_threads) {
  PL_ASSERT(state_size_ > 0);
  PL_ASSERT(states_ == nullptr);
  num_threads_ = num_threads;
  uint32_t alloc_size = num_threads_ * state_size_;
  states_ = reinterpret_cast<char *>(pool_.Allocate(alloc_size));
  PL_MEMSET(states_, 0, alloc_size);
}

char *ExecutorContext::ThreadStates::AccessThreadState(
    const uint32_t thread_id) const {
  PL_ASSERT(state_size_ > 0);
  PL_ASSERT(states_ != nullptr);
  PL_ASSERT(thread_id < num_threads_);
  return states_ + (thread_id * state_size_);
}

}  // namespace executor
}  // namespace peloton
<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#include <crossbow/program_options.hpp>
#include <crossbow/logger.hpp>

#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>

#include <common/Util.hpp>

#include "Client.hpp"

using namespace crossbow::program_options;
using namespace boost::asio;
using err_code = boost::system::error_code;

int main(int argc, const char** argv) {
    bool help = false;
    bool populate = false;
    bool useCHTables = false;
    int16_t numWarehouses = 1;
    crossbow::string host;
    std::string port("8713");
    std::string logLevel("DEBUG");
    std::string outFile("out.csv");
    size_t numClients = 1;
    unsigned time = 5*60;
    bool exit = false;
    auto opts = create_options("tpcc_server",
            value<'h'>("help", &help, tag::description{"print help"})
            , value<'H'>("host", &host, tag::description{"Comma-separated list of hosts"})
            , value<'l'>("log-level", &logLevel, tag::description{"The log level"})
            , value<'c'>("num-clients", &numClients, tag::description{"Number of Clients to run per host"})
            , value<'P'>("populate", &populate, tag::description{"Populate the database"})
            , value<'W'>("num-warehouses", &numWarehouses, tag::description{"Number of warehouses"})
            , value<'t'>("time", &time, tag::description{"Duration of the benchmark in seconds"})
            , value<'o'>("out", &outFile, tag::description{"Path to the output file"})
            , value<-1>("exit", &exit, tag::description{"Quit server"})
            , value<'a'>("ch-bench-analytics", &useCHTables,
                         tag::description{"Populate the database witht he additional tables used in the CHBenchmark"})
            );
    try {
        parse(opts, argc, argv);
    } catch (argument_not_found& e) {
        std::cerr << e.what() << std::endl << std::endl;
        print_help(std::cout, opts);
        return 1;
    }
    if (help) {
        print_help(std::cout, opts);
        return 0;
    }
    if (host.empty()) {
        std::cerr << "No host\n";
        return 1;
    }
    auto startTime = tpcc::Clock::now();
    auto endTime = startTime + std::chrono::seconds(time);
    crossbow::logger::logger->config.level = crossbow::logger::logLevelFromString(logLevel);
    try {
        auto hosts = tpcc::split(host.c_str(), ',');
        io_service service;
        auto sumClients = hosts.size() * numClients;
        std::vector<tpcc::Client> clients;
        clients.reserve(sumClients);
        auto wareHousesPerClient = numWarehouses / sumClients;
        for (decltype(sumClients) i = 0; i < sumClients; ++i) {
            if (i >= unsigned(numWarehouses)) break;
            clients.emplace_back(service, numWarehouses, int16_t(wareHousesPerClient * i + 1), int16_t(wareHousesPerClient * (i + 1)), endTime);
        }
        for (size_t i = 0; i < hosts.size(); ++i) {
            auto h = hosts[i];
            auto addr = tpcc::split(h, ':');
            assert(addr.size() <= 2);
            auto p = addr.size() == 2 ? addr[1] : port;
            ip::tcp::resolver resolver(service);
            ip::tcp::resolver::iterator iter;
            if (host == "") {
                iter = resolver.resolve(ip::tcp::resolver::query(p));
            } else {
                iter = resolver.resolve(ip::tcp::resolver::query(addr[0], p));
            }
            for (unsigned j = 0; j < numClients; ++j) {
                LOG_INFO("Connected to client " + crossbow::to_string(i*numClients + j));
                boost::asio::connect(clients[i*numClients + j].socket(), iter);
            }
        }

        {
            auto t = std::time(nullptr);
            std::string dateString(20, '\0');
            auto len = std::strftime(&dateString.front(), 20, "%T", std::localtime(&t));
            dateString.resize(len);
            LOG_INFO("Starting client at %1%", dateString);
        }

        if (exit) {
            for (auto& client : clients) {
                auto& cmds = client.commands();
                cmds.execute<tpcc::Command::EXIT>([](const err_code& ec){
                    if (ec) {
                        std::cerr << "ERROR: " << ec.message() << std::endl;
                    }
                });
                goto END;
            }
        }

        if (populate) {
            auto& cmds = clients[0].commands();
            cmds.execute<tpcc::Command::CREATE_SCHEMA>(
                    [&clients, &useCHTables](const err_code& ec,
                        const std::tuple<bool, crossbow::string>& res){
                if (ec) {
                    LOG_ERROR(ec.message());
                    return;
                }
                if (!std::get<0>(res)) {
                    LOG_ERROR(std::get<1>(res));
                    return;
                }

                auto& cmds = clients[0].commands();
                cmds.execute<tpcc::Command::POPULATE_DIM_TABLES>([&clients, &useCHTables](const err_code& ec, const std::tuple<bool, crossbow::string>& res){
                    if (ec) {
                        LOG_ERROR(ec.message());
                        return;
                    }
                    if (!std::get<0>(res)) {
                        LOG_ERROR(std::get<1>(res));
                        return;
                    }

                    for (auto& client : clients) {
                        client.populate(useCHTables);
                    }
                }, useCHTables);
            }, useCHTables);
        } else {
            for (decltype(clients.size()) i = 0; i < clients.size(); ++i) {
                auto& client = clients[i];
                client.run();
            }
        }
END:
        service.run();
        LOG_INFO("Done, writing results");
        std::ofstream out(outFile.c_str());
        out << "start,end,transaction,success,error\n";
        for (const auto& client : clients) {
            const auto& queue = client.log();
            for (const auto& e : queue) {
                crossbow::string tName;
                switch (e.transaction) {
                case tpcc::Command::POPULATE_WAREHOUSE:
                    tName = "Populate";
                    break;
                case tpcc::Command::POPULATE_DIM_TABLES:
                    tName = "Populate";
                    break;
                case tpcc::Command::CREATE_SCHEMA:
                    tName = "Schema Create";
                    break;
                case tpcc::Command::STOCK_LEVEL:
                    tName = "Stock Level";
                    break;
                case tpcc::Command::DELIVERY:
                    tName = "Delivery";
                    break;
                case tpcc::Command::NEW_ORDER:
                    tName = "New Order";
                    break;
                case tpcc::Command::ORDER_STATUS:
                    tName = "Order Status";
                    break;
                case tpcc::Command::PAYMENT:
                    tName = "Payment";
                    break;
                case tpcc::Command::EXIT:
                    assert(false);
                    break;
                }
                out << std::chrono::duration_cast<std::chrono::milliseconds>(e.start - startTime).count() << ','
                    << std::chrono::duration_cast<std::chrono::milliseconds>(e.end - startTime).count() << ','
                    << tName << ','
                    << (e.success ? "true" : "false") << ','
                    << e.error << std::endl;
            }
        }
        std::cout << '\a';
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}
<commit_msg>eliminated typo in main<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#include <crossbow/program_options.hpp>
#include <crossbow/logger.hpp>

#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <string>
#include <iostream>
#include <cassert>
#include <fstream>

#include <common/Util.hpp>

#include "Client.hpp"

using namespace crossbow::program_options;
using namespace boost::asio;
using err_code = boost::system::error_code;

int main(int argc, const char** argv) {
    bool help = false;
    bool populate = false;
    bool useCHTables = false;
    int16_t numWarehouses = 1;
    crossbow::string host;
    std::string port("8713");
    std::string logLevel("DEBUG");
    std::string outFile("out.csv");
    size_t numClients = 1;
    unsigned time = 5*60;
    bool exit = false;
    auto opts = create_options("tpcc_client",
            value<'h'>("help", &help, tag::description{"print help"})
            , value<'H'>("host", &host, tag::description{"Comma-separated list of hosts"})
            , value<'l'>("log-level", &logLevel, tag::description{"The log level"})
            , value<'c'>("num-clients", &numClients, tag::description{"Number of Clients to run per host"})
            , value<'P'>("populate", &populate, tag::description{"Populate the database"})
            , value<'W'>("num-warehouses", &numWarehouses, tag::description{"Number of warehouses"})
            , value<'t'>("time", &time, tag::description{"Duration of the benchmark in seconds"})
            , value<'o'>("out", &outFile, tag::description{"Path to the output file"})
            , value<-1>("exit", &exit, tag::description{"Quit server"})
            , value<'a'>("ch-bench-analytics", &useCHTables,
                         tag::description{"Populate the database witht he additional tables used in the CHBenchmark"})
            );
    try {
        parse(opts, argc, argv);
    } catch (argument_not_found& e) {
        std::cerr << e.what() << std::endl << std::endl;
        print_help(std::cout, opts);
        return 1;
    }
    if (help) {
        print_help(std::cout, opts);
        return 0;
    }
    if (host.empty()) {
        std::cerr << "No host\n";
        return 1;
    }
    auto startTime = tpcc::Clock::now();
    auto endTime = startTime + std::chrono::seconds(time);
    crossbow::logger::logger->config.level = crossbow::logger::logLevelFromString(logLevel);
    try {
        auto hosts = tpcc::split(host.c_str(), ',');
        io_service service;
        auto sumClients = hosts.size() * numClients;
        std::vector<tpcc::Client> clients;
        clients.reserve(sumClients);
        auto wareHousesPerClient = numWarehouses / sumClients;
        for (decltype(sumClients) i = 0; i < sumClients; ++i) {
            if (i >= unsigned(numWarehouses)) break;
            clients.emplace_back(service, numWarehouses, int16_t(wareHousesPerClient * i + 1), int16_t(wareHousesPerClient * (i + 1)), endTime);
        }
        for (size_t i = 0; i < hosts.size(); ++i) {
            auto h = hosts[i];
            auto addr = tpcc::split(h, ':');
            assert(addr.size() <= 2);
            auto p = addr.size() == 2 ? addr[1] : port;
            ip::tcp::resolver resolver(service);
            ip::tcp::resolver::iterator iter;
            if (host == "") {
                iter = resolver.resolve(ip::tcp::resolver::query(p));
            } else {
                iter = resolver.resolve(ip::tcp::resolver::query(addr[0], p));
            }
            for (unsigned j = 0; j < numClients; ++j) {
                LOG_INFO("Connected to client " + crossbow::to_string(i*numClients + j));
                boost::asio::connect(clients[i*numClients + j].socket(), iter);
            }
        }

        {
            auto t = std::time(nullptr);
            std::string dateString(20, '\0');
            auto len = std::strftime(&dateString.front(), 20, "%T", std::localtime(&t));
            dateString.resize(len);
            LOG_INFO("Starting client at %1%", dateString);
        }

        if (exit) {
            for (auto& client : clients) {
                auto& cmds = client.commands();
                cmds.execute<tpcc::Command::EXIT>([](const err_code& ec){
                    if (ec) {
                        std::cerr << "ERROR: " << ec.message() << std::endl;
                    }
                });
                goto END;
            }
        }

        if (populate) {
            auto& cmds = clients[0].commands();
            cmds.execute<tpcc::Command::CREATE_SCHEMA>(
                    [&clients, &useCHTables](const err_code& ec,
                        const std::tuple<bool, crossbow::string>& res){
                if (ec) {
                    LOG_ERROR(ec.message());
                    return;
                }
                if (!std::get<0>(res)) {
                    LOG_ERROR(std::get<1>(res));
                    return;
                }

                auto& cmds = clients[0].commands();
                cmds.execute<tpcc::Command::POPULATE_DIM_TABLES>([&clients, &useCHTables](const err_code& ec, const std::tuple<bool, crossbow::string>& res){
                    if (ec) {
                        LOG_ERROR(ec.message());
                        return;
                    }
                    if (!std::get<0>(res)) {
                        LOG_ERROR(std::get<1>(res));
                        return;
                    }

                    for (auto& client : clients) {
                        client.populate(useCHTables);
                    }
                }, useCHTables);
            }, useCHTables);
        } else {
            for (decltype(clients.size()) i = 0; i < clients.size(); ++i) {
                auto& client = clients[i];
                client.run();
            }
        }
END:
        service.run();
        LOG_INFO("Done, writing results");
        std::ofstream out(outFile.c_str());
        out << "start,end,transaction,success,error\n";
        for (const auto& client : clients) {
            const auto& queue = client.log();
            for (const auto& e : queue) {
                crossbow::string tName;
                switch (e.transaction) {
                case tpcc::Command::POPULATE_WAREHOUSE:
                    tName = "Populate";
                    break;
                case tpcc::Command::POPULATE_DIM_TABLES:
                    tName = "Populate";
                    break;
                case tpcc::Command::CREATE_SCHEMA:
                    tName = "Schema Create";
                    break;
                case tpcc::Command::STOCK_LEVEL:
                    tName = "Stock Level";
                    break;
                case tpcc::Command::DELIVERY:
                    tName = "Delivery";
                    break;
                case tpcc::Command::NEW_ORDER:
                    tName = "New Order";
                    break;
                case tpcc::Command::ORDER_STATUS:
                    tName = "Order Status";
                    break;
                case tpcc::Command::PAYMENT:
                    tName = "Payment";
                    break;
                case tpcc::Command::EXIT:
                    assert(false);
                    break;
                }
                out << std::chrono::duration_cast<std::chrono::milliseconds>(e.start - startTime).count() << ','
                    << std::chrono::duration_cast<std::chrono::milliseconds>(e.end - startTime).count() << ','
                    << tName << ','
                    << (e.success ? "true" : "false") << ','
                    << e.error << std::endl;
            }
        }
        std::cout << '\a';
    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}
<|endoftext|>