<commit_before>#include <stan/math/prim.hpp>
#include <gtest/gtest.h>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions.hpp>
#include <vector>

TEST(ProbDistributions, Dirichlet) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> theta(3, 1);
  theta << 0.2, 0.3, 0.5;
  Matrix<double, Dynamic, 1> alpha(3, 1);
  alpha << 1.0, 1.0, 1.0;
  EXPECT_FLOAT_EQ(0.6931472, stan::math::dirichlet_log(theta, alpha));

  Matrix<double, Dynamic, 1> theta2(4, 1);
  theta2 << 0.01, 0.01, 0.8, 0.18;
  Matrix<double, Dynamic, 1> alpha2(4, 1);
  alpha2 << 10.5, 11.5, 19.3, 5.1;
  EXPECT_FLOAT_EQ(-43.40045, stan::math::dirichlet_log(theta2, alpha2));
}

TEST(ProbDistributions, DirichletVectorised) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  using stan::math::dirichlet_log;
  Matrix<double, Dynamic, 1> theta1(3, 1), theta2(3, 1), theta3(3, 1);
  theta1 << 0.2, 0.3, 0.5;
  theta2 << 0.1, 0.8, 0.1;
  theta3 << 0.6, 0.1, 0.3;

  Matrix<double, Dynamic, 1> alpha1(3, 1), alpha2(3, 1), alpha3(3, 1);
  alpha1 << 1.0, 1.0, 1.0;
  alpha2 << 6.2, 3.5, 9.1;
  alpha3 << 2.5, 7.4, 6.1;

  std::vector<Matrix<double, Dynamic, 1>> theta_vec(3);
  theta_vec[0] = theta1;
  theta_vec[1] = theta2;
  theta_vec[2] = theta3;

  std::vector<Matrix<double, Dynamic, 1>> alpha_vec(3);
  alpha_vec[0] = alpha1;
  alpha_vec[1] = alpha2;
  alpha_vec[2] = alpha3;

  Matrix<double, Dynamic, 1> result(3);
  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta2, alpha2);
  result[2] = dirichlet_log(theta3, alpha3);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta_vec, alpha_vec));

  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta2, alpha1);
  result[2] = dirichlet_log(theta3, alpha1);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta_vec, alpha1));

  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta1, alpha2);
  result[2] = dirichlet_log(theta1, alpha3);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta1, alpha_vec));
}

TEST(ProbDistributions, DirichletPropto) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> theta(3, 1);
  theta << 0.2, 0.3, 0.5;
  Matrix<double, Dynamic, 1> alpha(3, 1);
  alpha << 1.0, 1.0, 1.0;
  EXPECT_FLOAT_EQ(0.0, stan::math::dirichlet_log<true>(theta, alpha));

  Matrix<double, Dynamic, 1> theta2(4, 1);
  theta2 << 0.01, 0.01, 0.8, 0.18;
  Matrix<double, Dynamic, 1> alpha2(4, 1);
  alpha2 << 10.5, 11.5, 19.3, 5.1;
  EXPECT_FLOAT_EQ(0.0, stan::math::dirichlet_log<true>(theta2, alpha2));
}

TEST(ProbDistributions, DirichletBounds) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> good_alpha(2, 1), bad_alpha(2, 1);
  Matrix<double, Dynamic, 1> good_theta(2, 1), bad_theta(2, 1);

  good_theta << 0.25, 0.75;
  good_alpha << 2, 3;
  EXPECT_NO_THROW(stan::math::dirichlet_log(good_theta, good_alpha));

  good_theta << 1.0, 0.0;
  good_alpha << 2, 3;
  EXPECT_NO_THROW(stan::math::dirichlet_log(good_theta, good_alpha))
      << "elements of theta can be 0";

  bad_theta << 0.25, 0.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "sum of theta is not 1";

  bad_theta << -0.25, 1.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "theta has element less than 0";

  bad_theta << -0.25, 1.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "theta has element less than 0";

  bad_alpha << 0.0, 1.0;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::domain_error)
      << "alpha has element equal to 0";

  bad_alpha << -0.5, 1.0;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::domain_error)
      << "alpha has element less than 0";

  bad_alpha = Matrix<double, Dynamic, 1>(4, 1);
  bad_alpha << 1, 2, 3, 4;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::invalid_argument)
      << "size mismatch: theta is a 2-vector, alpha is a 4-vector";
}

double chi_square(std::vector<int> bin, std::vector<double> expect) {
  double chi = 0;
  for (size_t j = 0; j < bin.size(); j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);
  return chi;
}

void test_dirichlet3_1(Eigen::VectorXd alpha) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  // bins 0 vs. 1 + 2
  boost::math::beta_distribution<> dist(alpha(0), alpha(1) + alpha(2));
  boost::math::chi_squared mydist(K - 1);

  std::vector<double> loc(K - 1);
  for (int i = 1; i < K; i++)
    loc[i - 1] = quantile(dist, i / static_cast<double>(K));

  std::vector<int> bin(K, 0);
  std::vector<double> expect(K, N / static_cast<double>(K));

  for (int count = 0; count < N; ++count) {
    Eigen::VectorXd theta = stan::math::dirichlet_rng(alpha, rng);
    int i;
    for (i = 0; i < K - 1 && theta(0) > loc[i]; ++i) {
    }
    ++bin[i];
  }
  EXPECT_TRUE(chi_square(bin, expect) < quantile(complement(mydist, 1e-6)));
}

void test_dirichlet3_2(VectorXd alpha) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));
  boost::math::beta_distribution<> dist(alpha(1), alpha(0) + alpha(2));
  boost::math::chi_squared mydist(K - 1);

  std::vector<double> loc(K - 1);
  for (size_t i = 0; i < loc.size(); i++)
    loc[i] = quantile(dist, (i + 1.0) / K);

  std::vector<int> bin(K, 0);
  std::vector<double> expect(K);
  for (int i = 0; i < K; i++)
    expect[i] = N / K;

  for (int count = 0; count < N; ++count) {
    VectorXd a = stan::math::dirichlet_rng(alpha, rng);
    int i = 0;
    while (i < K - 1 && a(1) > loc[i])
      ++i;
    ++bin[i];
  }

  EXPECT_TRUE(chi_square(bin, expect) < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsDirichlet, rngTest) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  VectorXd alpha(3);
  alpha << 2.0, 3.0, 11.0;
  test_dirichlet3_1(alpha);
  test_dirichlet3_2(alpha);

  VectorXd beta(3);
  beta << 0.1, 0.01, 0.2;
  test_dirichlet3_1(beta);
  test_dirichlet3_2(beta);
}

TEST(ProbDistributionsDirichlet, random) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  boost::random::mt19937 rng;
  VectorXd alpha(3);
  alpha << 2.0, 3.0, 11.0;
  EXPECT_NO_THROW(stan::math::dirichlet_rng(alpha, rng));

  VectorXd beta(3);
  beta << 0.001, 0.0001, 1e-10;
  EXPECT_NO_THROW(stan::math::dirichlet_rng(beta, rng));
}
<commit_msg>fix eigen namespace<commit_after>#include <stan/math/prim.hpp>
#include <gtest/gtest.h>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions.hpp>
#include <vector>

TEST(ProbDistributions, Dirichlet) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> theta(3, 1);
  theta << 0.2, 0.3, 0.5;
  Matrix<double, Dynamic, 1> alpha(3, 1);
  alpha << 1.0, 1.0, 1.0;
  EXPECT_FLOAT_EQ(0.6931472, stan::math::dirichlet_log(theta, alpha));

  Matrix<double, Dynamic, 1> theta2(4, 1);
  theta2 << 0.01, 0.01, 0.8, 0.18;
  Matrix<double, Dynamic, 1> alpha2(4, 1);
  alpha2 << 10.5, 11.5, 19.3, 5.1;
  EXPECT_FLOAT_EQ(-43.40045, stan::math::dirichlet_log(theta2, alpha2));
}

TEST(ProbDistributions, DirichletVectorised) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  using stan::math::dirichlet_log;
  Matrix<double, Dynamic, 1> theta1(3, 1), theta2(3, 1), theta3(3, 1);
  theta1 << 0.2, 0.3, 0.5;
  theta2 << 0.1, 0.8, 0.1;
  theta3 << 0.6, 0.1, 0.3;

  Matrix<double, Dynamic, 1> alpha1(3, 1), alpha2(3, 1), alpha3(3, 1);
  alpha1 << 1.0, 1.0, 1.0;
  alpha2 << 6.2, 3.5, 9.1;
  alpha3 << 2.5, 7.4, 6.1;

  std::vector<Matrix<double, Dynamic, 1>> theta_vec(3);
  theta_vec[0] = theta1;
  theta_vec[1] = theta2;
  theta_vec[2] = theta3;

  std::vector<Matrix<double, Dynamic, 1>> alpha_vec(3);
  alpha_vec[0] = alpha1;
  alpha_vec[1] = alpha2;
  alpha_vec[2] = alpha3;

  Matrix<double, Dynamic, 1> result(3);
  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta2, alpha2);
  result[2] = dirichlet_log(theta3, alpha3);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta_vec, alpha_vec));

  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta2, alpha1);
  result[2] = dirichlet_log(theta3, alpha1);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta_vec, alpha1));

  result[0] = dirichlet_log(theta1, alpha1);
  result[1] = dirichlet_log(theta1, alpha2);
  result[2] = dirichlet_log(theta1, alpha3);

  EXPECT_FLOAT_EQ(result.sum(), dirichlet_log(theta1, alpha_vec));
}

TEST(ProbDistributions, DirichletPropto) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> theta(3, 1);
  theta << 0.2, 0.3, 0.5;
  Matrix<double, Dynamic, 1> alpha(3, 1);
  alpha << 1.0, 1.0, 1.0;
  EXPECT_FLOAT_EQ(0.0, stan::math::dirichlet_log<true>(theta, alpha));

  Matrix<double, Dynamic, 1> theta2(4, 1);
  theta2 << 0.01, 0.01, 0.8, 0.18;
  Matrix<double, Dynamic, 1> alpha2(4, 1);
  alpha2 << 10.5, 11.5, 19.3, 5.1;
  EXPECT_FLOAT_EQ(0.0, stan::math::dirichlet_log<true>(theta2, alpha2));
}

TEST(ProbDistributions, DirichletBounds) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  Matrix<double, Dynamic, 1> good_alpha(2, 1), bad_alpha(2, 1);
  Matrix<double, Dynamic, 1> good_theta(2, 1), bad_theta(2, 1);

  good_theta << 0.25, 0.75;
  good_alpha << 2, 3;
  EXPECT_NO_THROW(stan::math::dirichlet_log(good_theta, good_alpha));

  good_theta << 1.0, 0.0;
  good_alpha << 2, 3;
  EXPECT_NO_THROW(stan::math::dirichlet_log(good_theta, good_alpha))
      << "elements of theta can be 0";

  bad_theta << 0.25, 0.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "sum of theta is not 1";

  bad_theta << -0.25, 1.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "theta has element less than 0";

  bad_theta << -0.25, 1.25;
  EXPECT_THROW(stan::math::dirichlet_log(bad_theta, good_alpha),
               std::domain_error)
      << "theta has element less than 0";

  bad_alpha << 0.0, 1.0;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::domain_error)
      << "alpha has element equal to 0";

  bad_alpha << -0.5, 1.0;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::domain_error)
      << "alpha has element less than 0";

  bad_alpha = Matrix<double, Dynamic, 1>(4, 1);
  bad_alpha << 1, 2, 3, 4;
  EXPECT_THROW(stan::math::dirichlet_log(good_theta, bad_alpha),
               std::invalid_argument)
      << "size mismatch: theta is a 2-vector, alpha is a 4-vector";
}

double chi_square(std::vector<int> bin, std::vector<double> expect) {
  double chi = 0;
  for (size_t j = 0; j < bin.size(); j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);
  return chi;
}

void test_dirichlet3_1(Eigen::VectorXd alpha) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  // bins 0 vs. 1 + 2
  boost::math::beta_distribution<> dist(alpha(0), alpha(1) + alpha(2));
  boost::math::chi_squared mydist(K - 1);

  std::vector<double> loc(K - 1);
  for (int i = 1; i < K; i++)
    loc[i - 1] = quantile(dist, i / static_cast<double>(K));

  std::vector<int> bin(K, 0);
  std::vector<double> expect(K, N / static_cast<double>(K));

  for (int count = 0; count < N; ++count) {
    Eigen::VectorXd theta = stan::math::dirichlet_rng(alpha, rng);
    int i;
    for (i = 0; i < K - 1 && theta(0) > loc[i]; ++i) {
    }
    ++bin[i];
  }
  EXPECT_TRUE(chi_square(bin, expect) < quantile(complement(mydist, 1e-6)));
}

void test_dirichlet3_2(Eigen::VectorXd alpha) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));
  boost::math::beta_distribution<> dist(alpha(1), alpha(0) + alpha(2));
  boost::math::chi_squared mydist(K - 1);

  std::vector<double> loc(K - 1);
  for (size_t i = 0; i < loc.size(); i++)
    loc[i] = quantile(dist, (i + 1.0) / K);

  std::vector<int> bin(K, 0);
  std::vector<double> expect(K);
  for (int i = 0; i < K; i++)
    expect[i] = N / K;

  for (int count = 0; count < N; ++count) {
    VectorXd a = stan::math::dirichlet_rng(alpha, rng);
    int i = 0;
    while (i < K - 1 && a(1) > loc[i])
      ++i;
    ++bin[i];
  }

  EXPECT_TRUE(chi_square(bin, expect) < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsDirichlet, rngTest) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  VectorXd alpha(3);
  alpha << 2.0, 3.0, 11.0;
  test_dirichlet3_1(alpha);
  test_dirichlet3_2(alpha);

  VectorXd beta(3);
  beta << 0.1, 0.01, 0.2;
  test_dirichlet3_1(beta);
  test_dirichlet3_2(beta);
}

TEST(ProbDistributionsDirichlet, random) {
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using Eigen::VectorXd;
  boost::random::mt19937 rng;
  VectorXd alpha(3);
  alpha << 2.0, 3.0, 11.0;
  EXPECT_NO_THROW(stan::math::dirichlet_rng(alpha, rng));

  VectorXd beta(3);
  beta << 0.001, 0.0001, 1e-10;
  EXPECT_NO_THROW(stan::math::dirichlet_rng(beta, rng));
}
<|endoftext|>


<commit_before>/*
** Copyright 2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include "com/centreon/broker/bam/configuration/state.hh"

using namespace com::centreon::broker::bam::configuration;

/**
 *  Constructor
 *
 */
state::state() {}

/**
 *  Copy constructor.
 *
 *  @param[in] right Object to copy.
 */
state::state(state const& right)
  : _bas(),
    _kpis(),
    _bool_expressions() {}

/**
 *  Destructor
 */
state::~state() {}

/**
 *  Assignment operator.
 *
 *  @param[in] right Object to copy.
 *
 *  @return This object.
 */
state& state::operator=(state const& right) {
  if (this != &right) {
    _bas = right._bas;
    _kpis= right._kpis;
    _bool_expressions= right._bool_expressions;
  }
  return (*this);
}

/**
 *  Get the list of BAs.
 *
 *  @return  A const list of all the business activities.
 */
state::bas const& state::get_bas() const {
  return (_bas);
}

/**
 *  Get all the kpis.
 *
 *  @return  A const list of kpis.
 */
state::kpis const& state::get_kpis() const {
  return (_kpis);
}

/**
 *  Get all the bool expressions.
 *
 *  @return  A list of constant expressions.
 */
state::bool_exps const& state::get_boolexps() const {
  return (_bool_expressions);
}

/**
 *  Get all the business activities
 *
 *  @return  The list of all the business activities.
 */
state::bas & state::get_bas() {
  return (_bas);
}

/**
 *  Get all the kpis
 *
 *  @return  A list of kpis.
 */
state::kpis & state::get_kpis() {
  return (_kpis);
}

/**
 *  Get all the boolexps
 *
 *  @return  A list of expressions.
 */
state::bool_exps& state::get_boolexps() {
  return (_bool_expressions);
}

<commit_msg>BAM: style update on configuration::state.<commit_after>/*
** Copyright 2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include "com/centreon/broker/bam/configuration/state.hh"

using namespace com::centreon::broker::bam::configuration;

/**
 *  Constructor
 *
 */
state::state() {}

/**
 *  Copy constructor.
 *
 *  @param[in] right Object to copy.
 */
state::state(state const& right)
  : _bas(right._bas),
    _kpis(right._kpis),
    _bool_expressions(right._bool_expressions) {}

/**
 *  Destructor
 */
state::~state() {}

/**
 *  Assignment operator.
 *
 *  @param[in] right Object to copy.
 *
 *  @return This object.
 */
state& state::operator=(state const& right) {
  if (this != &right) {
    _bas = right._bas;
    _kpis= right._kpis;
    _bool_expressions= right._bool_expressions;
  }
  return (*this);
}

/**
 *  Get the list of BAs.
 *
 *  @return  A const list of all the business activities.
 */
state::bas const& state::get_bas() const {
  return (_bas);
}

/**
 *  Get all the kpis.
 *
 *  @return  A const list of kpis.
 */
state::kpis const& state::get_kpis() const {
  return (_kpis);
}

/**
 *  Get all the bool expressions.
 *
 *  @return  A list of constant expressions.
 */
state::bool_exps const& state::get_boolexps() const {
  return (_bool_expressions);
}

/**
 *  Get all the business activities
 *
 *  @return  The list of all the business activities.
 */
state::bas & state::get_bas() {
  return (_bas);
}

/**
 *  Get all the kpis
 *
 *  @return  A list of kpis.
 */
state::kpis & state::get_kpis() {
  return (_kpis);
}

/**
 *  Get all the boolexps
 *
 *  @return  A list of expressions.
 */
state::bool_exps& state::get_boolexps() {
  return (_bool_expressions);
}

<|endoftext|>


<commit_before>/*
 * Copyright 2022 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/private/SkHalf.h"
#include "src/core/SkPipelineData.h"
#include "src/core/SkSLTypeShared.h"
#include "src/core/SkUniform.h"
#include "src/gpu/graphite/UniformManager.h"
#include "tests/Test.h"

using Layout = skgpu::graphite::Layout;
using UniformManager = skgpu::graphite::UniformManager;

static constexpr Layout kLayouts[] = {
        Layout::kStd140,
        Layout::kStd430,
        Layout::kMetal,
};

// This list excludes SkSLTypes that we don't support in uniforms, like Bool, UInt or UShort.
static constexpr SkSLType kTypes[] = {
        SkSLType::kShort,    SkSLType::kShort2,   SkSLType::kShort3,   SkSLType::kShort4,   //
        SkSLType::kFloat,    SkSLType::kFloat2,   SkSLType::kFloat3,   SkSLType::kFloat4,   //
        SkSLType::kHalf,     SkSLType::kHalf2,    SkSLType::kHalf3,    SkSLType::kHalf4,    //
        SkSLType::kInt,      SkSLType::kInt2,     SkSLType::kInt3,     SkSLType::kInt4,     //
        SkSLType::kFloat2x2, SkSLType::kFloat3x3, SkSLType::kFloat4x4,                      //
        SkSLType::kHalf2x2,  SkSLType::kHalf3x3,  SkSLType::kHalf4x4,
};

static constexpr float kFloats[16] = { 1.0f,  2.0f,  3.0f,  4.0f,
                                       5.0f,  6.0f,  7.0f,  8.0f,
                                       9.0f, 10.0f, 11.0f, 12.0f,
                                      13.0f, 14.0f, 15.0f, 16.0f };

static constexpr SkHalf kHalfs[16] = { 0x3C00, 0x4000, 0x4200, 0x4400,
                                       0x4500, 0x4600, 0x4700, 0x4800,
                                       0x4880, 0x4900, 0x4980, 0x4A00,
                                       0x4A80, 0x4B00, 0x4B80, 0x4C00 };

DEF_TEST(UniformManagerCheckSingleUniform, r) {
    // Verify that the uniform manager can hold all the basic uniform types, in every layout.
    for (Layout layout : kLayouts) {
        UniformManager mgr(layout);

        for (SkSLType type : kTypes) {
            const SkUniform expectations[] = {{"uniform", type}};
            mgr.setExpectedUniforms(SkSpan(expectations));
            mgr.write(type, 1, kFloats);
            REPORTER_ASSERT(r, mgr.size() > 0);
            mgr.reset();
        }
    }
}

DEF_TEST(UniformManagerCheckFloatEncoding, r) {
    // Verify that the uniform manager encodes float data properly.
    for (Layout layout : kLayouts) {
        UniformManager mgr(layout);

        for (SkSLType type : kTypes) {
            // Only test scalar and vector floats. (Matrices can introduce padding between values.)
            int vecLength = SkSLTypeVecLength(type);
            if (!SkSLTypeIsFloatType(type) || vecLength < 0) {
                continue;
            }

            // Write our uniform float scalar/vector.
            const SkUniform expectations[] = {{"uniform", type}};
            mgr.setExpectedUniforms(SkSpan(expectations));
            mgr.write(type, 1, kFloats);

            // Read back the uniform data.
            SkUniformDataBlock uniformData = mgr.peekData();
            if (layout == Layout::kMetal && !SkSLTypeIsFullPrecisionNumericType(type)) {
                // Metal should encode half-precision float uniforms in 16-bit floats.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(SkHalf));
                REPORTER_ASSERT(r, 0 == memcmp(kHalfs, uniformData.data(),
                                               vecLength * sizeof(SkHalf)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            } else {
                // Other layouts should always encode float uniforms in full precision.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(float));
                REPORTER_ASSERT(r, 0 == memcmp(kFloats, uniformData.data(),
                                               vecLength * sizeof(float)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            }

            mgr.reset();
        }
    }
}
<commit_msg>Add unit test verifying int encoding of uniforms.<commit_after>/*
 * Copyright 2022 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/private/SkHalf.h"
#include "src/core/SkPipelineData.h"
#include "src/core/SkSLTypeShared.h"
#include "src/core/SkUniform.h"
#include "src/gpu/graphite/UniformManager.h"
#include "tests/Test.h"

using Layout = skgpu::graphite::Layout;
using UniformManager = skgpu::graphite::UniformManager;

static constexpr Layout kLayouts[] = {
        Layout::kStd140,
        Layout::kStd430,
        Layout::kMetal,
};

// This list excludes SkSLTypes that we don't support in uniforms, like Bool, UInt or UShort.
static constexpr SkSLType kTypes[] = {
        SkSLType::kShort,    SkSLType::kShort2,   SkSLType::kShort3,   SkSLType::kShort4,   //
        SkSLType::kFloat,    SkSLType::kFloat2,   SkSLType::kFloat3,   SkSLType::kFloat4,   //
        SkSLType::kHalf,     SkSLType::kHalf2,    SkSLType::kHalf3,    SkSLType::kHalf4,    //
        SkSLType::kInt,      SkSLType::kInt2,     SkSLType::kInt3,     SkSLType::kInt4,     //
        SkSLType::kFloat2x2, SkSLType::kFloat3x3, SkSLType::kFloat4x4,                      //
        SkSLType::kHalf2x2,  SkSLType::kHalf3x3,  SkSLType::kHalf4x4,
};

static constexpr float kFloats[16] = { 1.0f,  2.0f,  3.0f,  4.0f,
                                       5.0f,  6.0f,  7.0f,  8.0f,
                                       9.0f, 10.0f, 11.0f, 12.0f,
                                      13.0f, 14.0f, 15.0f, 16.0f };

static constexpr SkHalf kHalfs[16] = { 0x3C00, 0x4000, 0x4200, 0x4400,
                                       0x4500, 0x4600, 0x4700, 0x4800,
                                       0x4880, 0x4900, 0x4980, 0x4A00,
                                       0x4A80, 0x4B00, 0x4B80, 0x4C00 };

static constexpr int16_t kShorts[16] = { 1,  -2,  3,  -4,
                                         5,  -6,  7,  -8,
                                         9, -10, 11, -12,
                                        13, -14, 15, -16 };

static constexpr int32_t kInts[16] = { 1,  -2,  3,  -4,
                                       5,  -6,  7,  -8,
                                       9, -10, 11, -12,
                                      13, -14, 15, -16 };

DEF_TEST(UniformManagerCheckSingleUniform, r) {
    // Verify that the uniform manager can hold all the basic uniform types, in every layout.
    for (Layout layout : kLayouts) {
        UniformManager mgr(layout);

        for (SkSLType type : kTypes) {
            const SkUniform expectations[] = {{"uniform", type}};
            mgr.setExpectedUniforms(SkSpan(expectations));
            mgr.write(type, 1, kFloats);
            REPORTER_ASSERT(r, mgr.size() > 0);
            mgr.reset();
        }
    }
}

DEF_TEST(UniformManagerCheckFloatEncoding, r) {
    // Verify that the uniform manager encodes float data properly.
    for (Layout layout : kLayouts) {
        UniformManager mgr(layout);

        for (SkSLType type : kTypes) {
            // Only test scalar and vector floats. (Matrices can introduce padding between values.)
            int vecLength = SkSLTypeVecLength(type);
            if (!SkSLTypeIsFloatType(type) || vecLength < 0) {
                continue;
            }

            // Write our uniform float scalar/vector.
            const SkUniform expectations[] = {{"uniform", type}};
            mgr.setExpectedUniforms(SkSpan(expectations));
            mgr.write(type, 1, kFloats);

            // Read back the uniform data.
            SkUniformDataBlock uniformData = mgr.peekData();
            if (layout == Layout::kMetal && !SkSLTypeIsFullPrecisionNumericType(type)) {
                // Metal should encode half-precision float uniforms in 16-bit floats.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(SkHalf));
                REPORTER_ASSERT(r, 0 == memcmp(kHalfs, uniformData.data(),
                                               vecLength * sizeof(SkHalf)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            } else {
                // Other layouts should always encode float uniforms in full precision.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(float));
                REPORTER_ASSERT(r, 0 == memcmp(kFloats, uniformData.data(),
                                               vecLength * sizeof(float)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            }

            mgr.reset();
        }
    }
}

DEF_TEST(UniformManagerCheckIntEncoding, r) {
    // Verify that the uniform manager encodes int data properly.
    for (Layout layout : kLayouts) {
        UniformManager mgr(layout);

        for (SkSLType type : kTypes) {
            if (!SkSLTypeIsIntegralType(type)) {
                continue;
            }

            // Write our uniform int scalar/vector.
            const SkUniform expectations[] = {{"uniform", type}};
            mgr.setExpectedUniforms(SkSpan(expectations));
            mgr.write(type, 1, kInts);

            // Read back the uniform data.
            SkUniformDataBlock uniformData = mgr.peekData();
            int vecLength = SkSLTypeVecLength(type);
            if (layout == Layout::kMetal && !SkSLTypeIsFullPrecisionNumericType(type)) {
                // Metal should encode short uniforms in 16 bits.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(int16_t));
                REPORTER_ASSERT(r, 0 == memcmp(kShorts, uniformData.data(),
                                               vecLength * sizeof(int16_t)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            } else {
                // Other layouts should always encode int uniforms in 32 bits.
                REPORTER_ASSERT(r, uniformData.size() >= vecLength * sizeof(int32_t));
                REPORTER_ASSERT(r, 0 == memcmp(kInts, uniformData.data(),
                                               vecLength * sizeof(int32_t)),
                                "Layout:%d Type:%d encoding failed", (int)layout, (int)type);
            }

            mgr.reset();
        }
    }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "Rest/Version.h"

#ifdef _WIN32
#include "Basics/win-utils.h"
#endif

#include <openssl/ssl.h>
#include <sstream>

#include <velocypack/Builder.h>
#include <velocypack/Version.h>
#include <velocypack/velocypack-aliases.h>

#include "Basics/StringUtils.h"
#include "Basics/Utf8Helper.h"
#include "Basics/build-date.h"
#include "Basics/build-repository.h"
#include "Basics/conversions.h"

#include <rocksdb/version.h>

using namespace arangodb::rest;

std::map<std::string, std::string> Version::Values;

////////////////////////////////////////////////////////////////////////////////
/// @brief parse a version string into major, minor
/// returns -1, -1 when the version string has an invalid format
/// returns major, -1 when only the major version can be determined
////////////////////////////////////////////////////////////////////////////////

std::pair<int, int> Version::parseVersionString(std::string const& str) {
  std::pair<int, int> result{ -1, -1 };

  if (!str.empty()) {
    char const* p = str.c_str();
    char const* q = p;
    while (*q >= '0' && *q <= '9') {
      ++q;
    }
    if (p != q) {
      result.first = std::stoi(std::string(p, q - p));
      result.second = 0;

      if (*q == '.') {
        ++q;
      }
      p = q;
      while (*q >= '0' && *q <= '9') {
        ++q;
      }
      if (p != q) {
        result.second = std::stoi(std::string(p, q - p));
      }
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief initialize
////////////////////////////////////////////////////////////////////////////////

void Version::initialize() {
  if (!Values.empty()) {
    return;
  }

  Values["architecture"] = (sizeof(void*) == 4 ? "32" : "64") + std::string("bit");
  Values["asm-crc32"] = (ENABLE_ASM_CRC32) ? "true" : "false";
  Values["boost-version"] = getBoostVersion();
  Values["build-date"] = getBuildDate();
  Values["compiler"] = getCompiler();
#ifdef _DEBUG
  Values["debug"] = "true";
#else
  Values["debug"] = "false";
#endif
  Values["endianness"] = getEndianness();
  Values["fd-setsize"] = arangodb::basics::StringUtils::itoa(FD_SETSIZE);
  Values["full-version-string"] = ARANGODB_VERSION_FULL;
  Values["icu-version"] = getICUVersion();
  Values["libev-version"] = getLibevVersion();
  Values["openssl-version"] = getOpenSSLVersion();
  Values["server-version"] = getServerVersion();
  Values["sizeof int"] = arangodb::basics::StringUtils::itoa(sizeof(int));
  Values["sizeof void*"] = arangodb::basics::StringUtils::itoa(sizeof(void*));
  Values["v8-version"] = getV8Version();
  Values["vpack-version"] = getVPackVersion();
  Values["zlib-version"] = getZLibVersion();

#if USE_ENTERPRISE
  Values["enterprise-version"] = ARANGODB_ENTERPRISE_VERSION;
#endif

#if HAVE_ARANGODB_BUILD_REPOSITORY
  Values["build-repository"] = getBuildRepository();
#endif

#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  Values["assertions"] = "true";
#else
  Values["assertions"] = "false";
#endif

  Values["rocksdb-version"] = std::to_string(ROCKSDB_MAJOR) + "." + std::to_string(ROCKSDB_MINOR) + "." + std::to_string(ROCKSDB_PATCH);

#ifdef __cplusplus
  Values["cplusplus"] = std::to_string(__cplusplus);
#else
  Values["cplusplus"] = "unknown";
#endif

#ifdef __SANITIZE_ADDRESS__
  Values["asan"] = "true";
#else
  Values["asan"] = "false";
#endif

#if defined(__SSE4_2__) && !defined(NO_SSE42)
  Values["sse42"] = "true";
#else
  Values["sse42"] = "false";
#endif

#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  Values["maintainer-mode"] = "true";
#else
  Values["maintainer-mode"] = "false";
#endif

#ifdef ARANGODB_ENABLE_FAILURE_TESTS
  Values["failure-tests"] = "true";
#else
  Values["failure-tests"] = "false";
#endif

#ifdef ARANGODB_HAVE_TCMALLOC
  Values["tcmalloc"] = "true";
#else
  Values["tcmalloc"] = "false";
#endif

#ifdef ARANGODB_HAVE_JEMALLOC
  Values["jemalloc"] = "true";
#else
  Values["jemalloc"] = "false";
#endif

#ifdef TRI_HAVE_POLL_H
  Values["fd-client-event-handler"] = "poll";
#else
  Values["fd-client-event-handler"] = "select";
#endif

  for (auto& it : Values) {
    arangodb::basics::StringUtils::trimInPlace(it.second);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get numeric server version
////////////////////////////////////////////////////////////////////////////////

int32_t Version::getNumericServerVersion() {
  char const* apiVersion = ARANGODB_VERSION;
  char const* p = apiVersion;

  // read major version
  while (*p >= '0' && *p <= '9') {
    ++p;
  }

  TRI_ASSERT(*p == '.');
  int32_t major = TRI_Int32String2(apiVersion, (p - apiVersion));

  apiVersion = ++p;

  // read minor version
  while (*p >= '0' && *p <= '9') {
    ++p;
  }

  TRI_ASSERT((*p == '.' || *p == '-' || *p == '\0') && p != apiVersion);
  int32_t minor = TRI_Int32String2(apiVersion, (p - apiVersion));

  return (int32_t)(minor * 100L + major * 10000L);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get server version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getServerVersion() { return std::string(ARANGODB_VERSION); }

////////////////////////////////////////////////////////////////////////////////
/// @brief get BOOST version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBoostVersion() {
#ifdef ARANGODB_BOOST_VERSION
  return std::string(ARANGODB_BOOST_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get V8 version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getV8Version() {
#ifdef ARANGODB_V8_VERSION
  return std::string(ARANGODB_V8_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get OpenSSL version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getOpenSSLVersion() {
#ifdef OPENSSL_VERSION_TEXT
  return std::string(OPENSSL_VERSION_TEXT);
#elif defined(ARANGODB_OPENSSL_VERSION)
  return std::string(ARANGODB_OPENSSL_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get libev version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getLibevVersion() {
#ifdef ARANGODB_LIBEV_VERSION
  return std::string(ARANGODB_LIBEV_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get vpack version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getVPackVersion() {
  return arangodb::velocypack::Version::BuildVersion.toString();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get zlib version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getZLibVersion() {
#ifdef ARANGODB_ZLIB_VERSION
  return std::string(ARANGODB_ZLIB_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get ICU version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getICUVersion() {
  UVersionInfo icuVersion;
  char icuVersionString[U_MAX_VERSION_STRING_LENGTH];
  u_getVersion(icuVersion);
  u_versionToString(icuVersion, icuVersionString);

  return icuVersionString;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get compiler
////////////////////////////////////////////////////////////////////////////////

std::string Version::getCompiler() {
#if defined(__clang__)
  return "clang [" + std::string(__VERSION__) + "]";
#elif defined(__GNUC__) || defined(__GNUG__)
  return "gcc [" + std::string(__VERSION__) + "]";
#elif defined(_MSC_VER)
  return "msvc [" + std::to_string(_MSC_VER) + "]";
#endif
  return "unknown";
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get endianness
////////////////////////////////////////////////////////////////////////////////

std::string Version::getEndianness() {
  uint64_t value = 0x12345678abcdef99;
  static_assert(sizeof(value) == 8, "unexpected uint64_t size");

  unsigned char const* p = reinterpret_cast<unsigned char const*>(&value);
  if (p[0] == 0x12 && p[1] == 0x34 && p[2] == 0x56 && p[3] == 0x78 && p[4] == 0xab && p[5] == 0xcd && p[6] == 0xef && p[7] == 0x99) {
    return "big";
  }

  if (p[0] == 0x99 && p[1] == 0xef && p[2] == 0xcd && p[3] == 0xab && p[4] == 0x78 && p[5] == 0x56 && p[6] == 0x34 && p[7] == 0x12) {
    return "little";
  }
  return "unknown";
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get build date
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBuildDate() {
// the OpenSuSE build system does not like it, if __DATE__ is used
#ifdef ARANGODB_BUILD_DATE
  return std::string(ARANGODB_BUILD_DATE);
#else
  return std::string(__DATE__).append(" ").append(__TIME__);
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get build repository
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBuildRepository() {
#ifdef HAVE_ARANGODB_BUILD_REPOSITORY
  return std::string(ARANGODB_BUILD_REPOSITORY);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief return a server version string
////////////////////////////////////////////////////////////////////////////////

std::string Version::getVerboseVersionString() {
  std::ostringstream version;

  version << "ArangoDB " << ARANGODB_VERSION_FULL << " "
          << (sizeof(void*) == 4 ? "32" : "64") << "bit"
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
          << " maintainer mode"
#endif
#ifdef __SANITIZE_ADDRESS__
          << " with ASAN"
#endif
          << ", using "
#ifdef TRI_HAVE_TCMALLOC
          << "tcmalloc, "
#endif
#ifdef TRI_HAVE_JEMALLOC
          << "jemalloc, "
#endif
          << "VPack " << getVPackVersion() << ", "
          << "ICU " << getICUVersion() << ", "
          << "V8 " << getV8Version() << ", " << getOpenSSLVersion();

  return version.str();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get detailed version information as a (multi-line) string
////////////////////////////////////////////////////////////////////////////////

std::string Version::getDetailed() {
  std::string result;

  for (auto& it : Values) {
    std::string const& value = it.second;

    if (!value.empty()) {
      result.append(it.first);
      result.append(": ");
      result.append(it.second);
#ifdef _WIN32
      result += "\r\n";
#else
      result += "\n";
#endif
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief VelocyPack all data
////////////////////////////////////////////////////////////////////////////////

void Version::getVPack(VPackBuilder& dst) {
  TRI_ASSERT(!dst.isClosed());

  for (auto const& it : Values) {
    std::string const& value = it.second;

    if (!value.empty()) {
      dst.add(it.first, VPackValue(value));
    }
  }
}

<commit_msg>more detailed version output<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "Rest/Version.h"

#ifdef _WIN32
#include "Basics/win-utils.h"
#endif

#include <openssl/ssl.h>
#include <sstream>

#include <velocypack/Builder.h>
#include <velocypack/Version.h>
#include <velocypack/velocypack-aliases.h>

#include "Basics/StringUtils.h"
#include "Basics/Utf8Helper.h"
#include "Basics/build-date.h"
#include "Basics/build-repository.h"
#include "Basics/conversions.h"

#include <rocksdb/version.h>

using namespace arangodb::rest;

std::map<std::string, std::string> Version::Values;

////////////////////////////////////////////////////////////////////////////////
/// @brief parse a version string into major, minor
/// returns -1, -1 when the version string has an invalid format
/// returns major, -1 when only the major version can be determined
////////////////////////////////////////////////////////////////////////////////

std::pair<int, int> Version::parseVersionString(std::string const& str) {
  std::pair<int, int> result{ -1, -1 };

  if (!str.empty()) {
    char const* p = str.c_str();
    char const* q = p;
    while (*q >= '0' && *q <= '9') {
      ++q;
    }
    if (p != q) {
      result.first = std::stoi(std::string(p, q - p));
      result.second = 0;

      if (*q == '.') {
        ++q;
      }
      p = q;
      while (*q >= '0' && *q <= '9') {
        ++q;
      }
      if (p != q) {
        result.second = std::stoi(std::string(p, q - p));
      }
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief initialize
////////////////////////////////////////////////////////////////////////////////

void Version::initialize() {
  if (!Values.empty()) {
    return;
  }

  Values["architecture"] = (sizeof(void*) == 4 ? "32" : "64") + std::string("bit");
  Values["asm-crc32"] = (ENABLE_ASM_CRC32) ? "true" : "false";
  Values["boost-version"] = getBoostVersion();
  Values["build-date"] = getBuildDate();
  Values["compiler"] = getCompiler();
#ifdef _DEBUG
  Values["debug"] = "true";
#else
  Values["debug"] = "false";
#endif
  Values["endianness"] = getEndianness();
  Values["fd-setsize"] = arangodb::basics::StringUtils::itoa(FD_SETSIZE);
  Values["full-version-string"] = ARANGODB_VERSION_FULL;
  Values["icu-version"] = getICUVersion();
  Values["libev-version"] = getLibevVersion();
  Values["openssl-version"] = getOpenSSLVersion();
  Values["platform"] = TRI_PLATFORM;
  Values["server-version"] = getServerVersion();
  Values["sizeof int"] = arangodb::basics::StringUtils::itoa(sizeof(int));
  Values["sizeof void*"] = arangodb::basics::StringUtils::itoa(sizeof(void*));
  Values["v8-version"] = getV8Version();
  Values["vpack-version"] = getVPackVersion();
  Values["zlib-version"] = getZLibVersion();

#if USE_ENTERPRISE
  Values["enterprise-version"] = ARANGODB_ENTERPRISE_VERSION;
#endif

#if HAVE_ARANGODB_BUILD_REPOSITORY
  Values["build-repository"] = getBuildRepository();
#endif

#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  Values["assertions"] = "true";
#else
  Values["assertions"] = "false";
#endif

  Values["rocksdb-version"] = std::to_string(ROCKSDB_MAJOR) + "." + std::to_string(ROCKSDB_MINOR) + "." + std::to_string(ROCKSDB_PATCH);

#ifdef __cplusplus
  Values["cplusplus"] = std::to_string(__cplusplus);
#else
  Values["cplusplus"] = "unknown";
#endif

#ifdef __SANITIZE_ADDRESS__
  Values["asan"] = "true";
#else
  Values["asan"] = "false";
#endif

#if defined(__SSE4_2__) && !defined(NO_SSE42)
  Values["sse42"] = "true";
#else
  Values["sse42"] = "false";
#endif

#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
  Values["maintainer-mode"] = "true";
#else
  Values["maintainer-mode"] = "false";
#endif

#ifdef ARANGODB_ENABLE_FAILURE_TESTS
  Values["failure-tests"] = "true";
#else
  Values["failure-tests"] = "false";
#endif

#ifdef ARANGODB_HAVE_TCMALLOC
  Values["tcmalloc"] = "true";
#else
  Values["tcmalloc"] = "false";
#endif

#ifdef ARANGODB_HAVE_JEMALLOC
  Values["jemalloc"] = "true";
#else
  Values["jemalloc"] = "false";
#endif

#ifdef TRI_HAVE_POLL_H
  Values["fd-client-event-handler"] = "poll";
#else
  Values["fd-client-event-handler"] = "select";
#endif

  for (auto& it : Values) {
    arangodb::basics::StringUtils::trimInPlace(it.second);
  }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get numeric server version
////////////////////////////////////////////////////////////////////////////////

int32_t Version::getNumericServerVersion() {
  char const* apiVersion = ARANGODB_VERSION;
  char const* p = apiVersion;

  // read major version
  while (*p >= '0' && *p <= '9') {
    ++p;
  }

  TRI_ASSERT(*p == '.');
  int32_t major = TRI_Int32String2(apiVersion, (p - apiVersion));

  apiVersion = ++p;

  // read minor version
  while (*p >= '0' && *p <= '9') {
    ++p;
  }

  TRI_ASSERT((*p == '.' || *p == '-' || *p == '\0') && p != apiVersion);
  int32_t minor = TRI_Int32String2(apiVersion, (p - apiVersion));

  return (int32_t)(minor * 100L + major * 10000L);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get server version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getServerVersion() { return std::string(ARANGODB_VERSION); }

////////////////////////////////////////////////////////////////////////////////
/// @brief get BOOST version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBoostVersion() {
#ifdef ARANGODB_BOOST_VERSION
  return std::string(ARANGODB_BOOST_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get V8 version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getV8Version() {
#ifdef ARANGODB_V8_VERSION
  return std::string(ARANGODB_V8_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get OpenSSL version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getOpenSSLVersion() {
#ifdef OPENSSL_VERSION_TEXT
  return std::string(OPENSSL_VERSION_TEXT);
#elif defined(ARANGODB_OPENSSL_VERSION)
  return std::string(ARANGODB_OPENSSL_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get libev version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getLibevVersion() {
#ifdef ARANGODB_LIBEV_VERSION
  return std::string(ARANGODB_LIBEV_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get vpack version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getVPackVersion() {
  return arangodb::velocypack::Version::BuildVersion.toString();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get zlib version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getZLibVersion() {
#ifdef ARANGODB_ZLIB_VERSION
  return std::string(ARANGODB_ZLIB_VERSION);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get ICU version
////////////////////////////////////////////////////////////////////////////////

std::string Version::getICUVersion() {
  UVersionInfo icuVersion;
  char icuVersionString[U_MAX_VERSION_STRING_LENGTH];
  u_getVersion(icuVersion);
  u_versionToString(icuVersion, icuVersionString);

  return icuVersionString;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get compiler
////////////////////////////////////////////////////////////////////////////////

std::string Version::getCompiler() {
#if defined(__clang__)
  return "clang [" + std::string(__VERSION__) + "]";
#elif defined(__GNUC__) || defined(__GNUG__)
  return "gcc [" + std::string(__VERSION__) + "]";
#elif defined(_MSC_VER)
  return "msvc [" + std::to_string(_MSC_VER) + "]";
#endif
  return "unknown";
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get endianness
////////////////////////////////////////////////////////////////////////////////

std::string Version::getEndianness() {
  uint64_t value = 0x12345678abcdef99;
  static_assert(sizeof(value) == 8, "unexpected uint64_t size");

  unsigned char const* p = reinterpret_cast<unsigned char const*>(&value);
  if (p[0] == 0x12 && p[1] == 0x34 && p[2] == 0x56 && p[3] == 0x78 && p[4] == 0xab && p[5] == 0xcd && p[6] == 0xef && p[7] == 0x99) {
    return "big";
  }

  if (p[0] == 0x99 && p[1] == 0xef && p[2] == 0xcd && p[3] == 0xab && p[4] == 0x78 && p[5] == 0x56 && p[6] == 0x34 && p[7] == 0x12) {
    return "little";
  }
  return "unknown";
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get build date
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBuildDate() {
// the OpenSuSE build system does not like it, if __DATE__ is used
#ifdef ARANGODB_BUILD_DATE
  return std::string(ARANGODB_BUILD_DATE);
#else
  return std::string(__DATE__).append(" ").append(__TIME__);
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get build repository
////////////////////////////////////////////////////////////////////////////////

std::string Version::getBuildRepository() {
#ifdef HAVE_ARANGODB_BUILD_REPOSITORY
  return std::string(ARANGODB_BUILD_REPOSITORY);
#else
  return std::string("");
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// @brief return a server version string
////////////////////////////////////////////////////////////////////////////////

std::string Version::getVerboseVersionString() {
  std::ostringstream version;

  version << "ArangoDB " << ARANGODB_VERSION_FULL << " "
          << (sizeof(void*) == 4 ? "32" : "64") << "bit"
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
          << " maintainer mode"
#endif
#ifdef __SANITIZE_ADDRESS__
          << " with ASAN"
#endif
          << ", using "
#ifdef TRI_HAVE_TCMALLOC
          << "tcmalloc, "
#endif
#ifdef TRI_HAVE_JEMALLOC
          << "jemalloc, "
#endif
          << "VPack " << getVPackVersion() << ", "
          << "ICU " << getICUVersion() << ", "
          << "V8 " << getV8Version() << ", " << getOpenSSLVersion();

  return version.str();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief get detailed version information as a (multi-line) string
////////////////////////////////////////////////////////////////////////////////

std::string Version::getDetailed() {
  std::string result;

  for (auto& it : Values) {
    std::string const& value = it.second;

    if (!value.empty()) {
      result.append(it.first);
      result.append(": ");
      result.append(it.second);
#ifdef _WIN32
      result += "\r\n";
#else
      result += "\n";
#endif
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief VelocyPack all data
////////////////////////////////////////////////////////////////////////////////

void Version::getVPack(VPackBuilder& dst) {
  TRI_ASSERT(!dst.isClosed());

  for (auto const& it : Values) {
    std::string const& value = it.second;

    if (!value.empty()) {
      dst.add(it.first, VPackValue(value));
    }
  }
}

<|endoftext|>


<commit_before>#include <cppunit/extensions/HelperMacros.h>
#include "spotty_network_failure_test.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <libcmm.h>
#include <libcmm_irob.h>
#include "net_interface.h"
#include "test_common.h"
#include "cmm_socket_control.h"
#include "libcmm_ipc.h"

#include <string>
#include <vector>
using std::string; using std::vector;

CPPUNIT_TEST_SUITE_REGISTRATION(SpottyNetworkFailureTest);

int
make_listening_socket(short port)
{
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    handle_error(sock < 0, "socket");

    int on = 1;
    int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                        (char *) &on, sizeof(on));
    if (rc < 0) {
        DEBUG_LOG("Cannot reuse socket address\n");
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    socklen_t addrlen = sizeof(addr);
    rc = bind(sock, (struct sockaddr*)&addr, addrlen);
    handle_error(rc < 0, "bind");

    rc = listen(sock, 5);
    handle_error(rc < 0, "cmm_listen");
    DEBUG_LOG("Receiver is listening...\n");

    return sock;
}

void
SpottyNetworkFailureTest::setupReceiver()
{
    listen_sock = make_listening_socket(TEST_PORT);
}

void
SpottyNetworkFailureTest::startReceiver()
{
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    int bootstrap_sock = accept(listen_sock,
                                (struct sockaddr *)&addr,
                                &addrlen);
    handle_error(bootstrap_sock < 0, "accept");
    DEBUG_LOG("Receiver accepted connection %d\n", bootstrap_sock);

    doFakeIntNWSetup(bootstrap_sock);
    close(bootstrap_sock);
}

void
SpottyNetworkFailureTest::doFakeIntNWSetup(int bootstrap_sock)
{
    struct CMMSocketControlHdr hello;
    int rc = read(bootstrap_sock, &hello, sizeof(hello));
    handle_error(rc != sizeof(hello), "receiving intnw hello");

    short intnw_listen_port = 42429;
    intnw_listen_sock = make_listening_socket(intnw_listen_port);
    handle_error(intnw_listen_sock < 0, "creating intnw listener socket");

    hello.op.hello.listen_port = htons(intnw_listen_port);
    hello.op.hello.num_ifaces = htonl(1);
    rc = write(bootstrap_sock, &hello, sizeof(hello));
    handle_error(rc != sizeof(hello), "sending intnw hello");

    acceptCsocks();
    exchangeNetworkInterfaces(bootstrap_sock);
}

struct net_interface init_csocket(int csock)
{
    struct CMMSocketControlHdr hdr;
    struct net_interface iface;
    int rc = read(csock, &hdr, sizeof(hdr));
    handle_error(rc != sizeof(hdr), "reading csock net_interface data");
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_NEW_INTERFACE);

    iface = hdr.op.new_interface;
    iface.bandwidth_down = ntohl(iface.bandwidth_down);
    iface.bandwidth_up = ntohl(iface.bandwidth_up);
    iface.RTT = ntohl(iface.RTT);

    memset(&hdr, 0, sizeof(hdr));
    hdr.type = htons(CMM_CONTROL_MSG_HELLO);
    rc = write(csock, &hdr, sizeof(hdr));
    handle_error(rc != sizeof(hdr), "sending csock confirmation");

    return iface;
}

void
SpottyNetworkFailureTest::acceptCsocks()
{
    struct net_interface steady_iface, intermittent_iface;

    steady_csock = accept(intnw_listen_sock, NULL, NULL);
    handle_error(steady_csock < 0, "accepting csocket");

    steady_iface = init_csocket(steady_csock);

    intermittent_csock = accept(intnw_listen_sock, NULL, NULL);
    handle_error(steady_csock < 0, "accepting csocket");

    intermittent_iface = init_csocket(intermittent_csock);

    // the better network is the intermittent one.
    if (steady_iface.RTT < intermittent_iface.RTT) {
        int tmpsock = steady_csock;
        steady_csock = intermittent_csock;
        intermittent_csock = tmpsock;
    }
}

void
SpottyNetworkFailureTest::exchangeNetworkInterfaces(int bootstrap_sock)
{
    struct net_interface sentinel;
    memset(&sentinel, 0, sizeof(sentinel));
    int rc;
    struct CMMSocketControlHdr hdr;
    do {
        rc = read(bootstrap_sock, &hdr, sizeof(hdr));
        handle_error(rc != sizeof(hdr), "reading net iface");
    } while (memcmp(&hdr.op.new_interface, &sentinel, sizeof(struct net_interface)) != 0);

    struct CMMSocketControlHdr hdrs[2];
    memset(hdrs, 0, sizeof(hdrs));
    hdrs[0].type = htons(CMM_CONTROL_MSG_NEW_INTERFACE);
    inet_aton("141.212.110.132", &hdrs[0].op.new_interface.ip_addr);
    hdrs[0].op.new_interface.bandwidth_down = 1250000;
    hdrs[0].op.new_interface.bandwidth_up = 1250000;
    hdrs[0].op.new_interface.RTT = 1;
    hdrs[1].type = htons(CMM_CONTROL_MSG_NEW_INTERFACE);
    hdrs[0].op.new_interface = sentinel;
    rc = write(bootstrap_sock, hdrs, sizeof(hdrs));
    handle_error(rc != sizeof(hdrs), "sending net iface");
}

void
SpottyNetworkFailureTest::startSender()
{
    // create connecting multisocket
    EndToEndTestsBase::startSender();
}

void
SpottyNetworkFailureTest::tearDown()
{
    if (isReceiver()) {
        close(steady_csock);
        close(intermittent_csock);
        close(intnw_listen_sock);
        close(listen_sock);
    } else {
        EndToEndTestsBase::tearDown();
    }
}

int
connect_to_scout_control()
{
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    handle_error(sock < 0, "creating scout control socket");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_port = htons(CONTROL_SOCKET_PORT);

    socklen_t addrlen = sizeof(addr);
    int rc = connect(sock, (struct sockaddr *)&addr, addrlen);
    handle_error(rc < 0, "connecting scout control socket");

    return sock;
}

void
SpottyNetworkFailureTest::testOneNetworkFails()
{
    const char expected_str[] = "ABCDEFGHIJ";
    const size_t len = strlen(expected_str);
    sleep(1);

    char buf[len + 1];
    memset(buf, 0, sizeof(buf));

    if (isReceiver()) {
        shutdown(intermittent_csock, SHUT_RDWR);

        // expected messages:
        //  in order:
        //  1) begin_irob
        //  2) irob_chunk
        //  3) end_irob
        //
        // at any time:
        //  - down_interface
        vector<struct CMMSocketControlHdr> hdrs;
        while (hdrs.size() < 4) {
            struct CMMSocketControlHdr hdr;
            memset(&hdr, 0, sizeof(hdr));
            int rc = recv(steady_csock, &hdr, sizeof(hdr), MSG_WAITALL);
            CPPUNIT_ASSERT_EQUAL((int)sizeof(hdr), rc);
            if (ntohs(hdr.type) == CMM_CONTROL_MSG_IROB_CHUNK) {
                CPPUNIT_ASSERT_EQUAL(len, ntohl(hdr.op.irob_chunk.datalen));

                rc = recv(steady_csock, buf, len, MSG_WAITALL);
                CPPUNIT_ASSERT_EQUAL(rc, (int)len);
                CPPUNIT_ASSERT_EQUAL(string(expected_str), string(buf));
            }

            hdrs.push_back(hdr);
        }
        CPPUNIT_ASSERT_EQUAL(4U, hdrs.size());

        irob_id_t irob_id = -1;
        bool begin_irob_recvd = false;
        bool irob_chunk_recvd = false;
        bool end_irob_recvd = false;
        bool down_interface_recvd = false;
        for (size_t i = 0; i < hdrs.size(); ++i) {
            switch (ntohs(hdrs[i].type)) {
            case CMM_CONTROL_MSG_BEGIN_IROB:
                irob_id = ntohl(hdrs[i].op.begin_irob.id);
                begin_irob_recvd = true; break;
            case CMM_CONTROL_MSG_IROB_CHUNK:
                irob_chunk_recvd = true; break;
            case CMM_CONTROL_MSG_END_IROB:
                end_irob_recvd = true; break;
            case CMM_CONTROL_MSG_DOWN_INTERFACE:
                down_interface_recvd = true; break;
            default:
                CPPUNIT_ASSERT(false);
            }
        }
        CPPUNIT_ASSERT(begin_irob_recvd);
        CPPUNIT_ASSERT(irob_chunk_recvd);
        CPPUNIT_ASSERT(end_irob_recvd);
        CPPUNIT_ASSERT(down_interface_recvd);

        struct CMMSocketControlHdr ack;
        memset(&ack, 0, sizeof(ack));
        ack.type = htons(CMM_CONTROL_MSG_ACK);
        ack.op.ack.id = htonl(irob_id);
        int rc = write(steady_csock, &ack, sizeof(ack));
        CPPUNIT_ASSERT_EQUAL((int)sizeof(ack), rc);

        char response[sizeof(struct CMMSocketControlHdr) * 3 + len];
        memset(response, 0, sizeof(response));
        struct CMMSocketControlHdr *begin_irob_hdr = (struct CMMSocketControlHdr *) response;
        struct CMMSocketControlHdr *irob_chunk_hdr =
            (struct CMMSocketControlHdr *) (((char *) begin_irob_hdr) + sizeof(*begin_irob_hdr));
        char *resp_data = ((char *) irob_chunk_hdr) + sizeof(*irob_chunk_hdr);
        struct CMMSocketControlHdr *end_irob_hdr =
            (struct CMMSocketControlHdr *) (resp_data + len);

        irob_id_t resp_irob_id = irob_id + 1;
        begin_irob_hdr->type = htons(CMM_CONTROL_MSG_BEGIN_IROB);
        begin_irob_hdr->op.begin_irob.id = htonl(resp_irob_id);
        irob_chunk_hdr->op.irob_chunk.id = htonl(resp_irob_id);
        irob_chunk_hdr->op.irob_chunk.seqno = 0;
        irob_chunk_hdr->op.irob_chunk.datalen = htonl(len);
        memcpy(resp_data, buf, len);
        end_irob_hdr->op.end_irob.id = htonl(resp_irob_id);
        end_irob_hdr->op.end_irob.expected_bytes = htonl(len);
        end_irob_hdr->op.end_irob.expected_chunks = htonl(1);

        rc = write(steady_csock, response, sizeof(response));
        CPPUNIT_ASSERT_EQUAL((int) sizeof(response), rc);

        memset(&ack, 0, sizeof(ack));
        rc = recv(steady_csock, &ack, sizeof(ack), MSG_WAITALL);
        CPPUNIT_ASSERT_EQUAL((int) sizeof(ack), rc);
        CPPUNIT_ASSERT_EQUAL((uint16_t) CMM_CONTROL_MSG_ACK, ntohs(ack.type));
        CPPUNIT_ASSERT_EQUAL(resp_irob_id, (irob_id_t) ntohl(ack.op.ack.id));
    } else {
        int scout_control_sock = connect_to_scout_control();

        sleep(1);
        int rc = cmm_write(data_sock, expected_str, len, 0, NULL, NULL);
        CPPUNIT_ASSERT_EQUAL((int)len, rc); // succeeds immediately without waiting for bytes to be sent

        sleep(1);
        char cmd[] = "bg_down\n";
        rc = write(scout_control_sock, cmd, strlen(cmd));
        CPPUNIT_ASSERT_EQUAL((int) strlen(cmd), rc);

        sleep(1);
        memset(buf, 0, sizeof(buf));
        rc = cmm_recv(data_sock, buf, len, MSG_WAITALL, NULL);
        CPPUNIT_ASSERT_EQUAL((int) len, rc);
        CPPUNIT_ASSERT_EQUAL(string(expected_str), string(buf));

        close(scout_control_sock);
    }
}
<commit_msg>Fixed conflict.<commit_after>#include <cppunit/extensions/HelperMacros.h>
#include "spotty_network_failure_test.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <string.h>
#include <libcmm.h>
#include <libcmm_irob.h>
#include "net_interface.h"
#include "test_common.h"
#include "cmm_socket_control.h"
#include "libcmm_ipc.h"

#include <string>
#include <vector>
using std::string; using std::vector;

CPPUNIT_TEST_SUITE_REGISTRATION(SpottyNetworkFailureTest);

int
make_listening_socket(short port)
{
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    handle_error(sock < 0, "socket");

    int on = 1;
    int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                        (char *) &on, sizeof(on));
    if (rc < 0) {
        DEBUG_LOG("Cannot reuse socket address\n");
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    socklen_t addrlen = sizeof(addr);
    rc = bind(sock, (struct sockaddr*)&addr, addrlen);
    handle_error(rc < 0, "bind");

    rc = listen(sock, 5);
    handle_error(rc < 0, "cmm_listen");
    DEBUG_LOG("Receiver is listening...\n");

    return sock;
}

void
SpottyNetworkFailureTest::setupReceiver()
{
    listen_sock = make_listening_socket(TEST_PORT);
}

void
SpottyNetworkFailureTest::startReceiver()
{
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    int bootstrap_sock = accept(listen_sock,
                                (struct sockaddr *)&addr,
                                &addrlen);
    handle_error(bootstrap_sock < 0, "accept");
    DEBUG_LOG("Receiver accepted connection %d\n", bootstrap_sock);

    doFakeIntNWSetup(bootstrap_sock);
    close(bootstrap_sock);
}

void
SpottyNetworkFailureTest::doFakeIntNWSetup(int bootstrap_sock)
{
    struct CMMSocketControlHdr hello;
    int rc = read(bootstrap_sock, &hello, sizeof(hello));
    handle_error(rc != sizeof(hello), "receiving intnw hello");

    short intnw_listen_port = 42429;
    intnw_listen_sock = make_listening_socket(intnw_listen_port);
    handle_error(intnw_listen_sock < 0, "creating intnw listener socket");

    hello.op.hello.listen_port = htons(intnw_listen_port);
    hello.op.hello.num_ifaces = htonl(1);
    rc = write(bootstrap_sock, &hello, sizeof(hello));
    handle_error(rc != sizeof(hello), "sending intnw hello");

    acceptCsocks();
    exchangeNetworkInterfaces(bootstrap_sock);
}

struct net_interface init_csocket(int csock)
{
    struct CMMSocketControlHdr hdr;
    struct net_interface iface;
    int rc = read(csock, &hdr, sizeof(hdr));
    handle_error(rc != sizeof(hdr), "reading csock net_interface data");
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_NEW_INTERFACE);

    iface = hdr.op.new_interface;
    iface.bandwidth_down = ntohl(iface.bandwidth_down);
    iface.bandwidth_up = ntohl(iface.bandwidth_up);
    iface.RTT = ntohl(iface.RTT);

    memset(&hdr, 0, sizeof(hdr));
    hdr.type = htons(CMM_CONTROL_MSG_HELLO);
    rc = write(csock, &hdr, sizeof(hdr));
    handle_error(rc != sizeof(hdr), "sending csock confirmation");

    return iface;
}

void
SpottyNetworkFailureTest::acceptCsocks()
{
    struct net_interface steady_iface, intermittent_iface;

    steady_csock = accept(intnw_listen_sock, NULL, NULL);
    handle_error(steady_csock < 0, "accepting csocket");

    steady_iface = init_csocket(steady_csock);

    intermittent_csock = accept(intnw_listen_sock, NULL, NULL);
    handle_error(steady_csock < 0, "accepting csocket");

    intermittent_iface = init_csocket(intermittent_csock);

    // the better network is the intermittent one.
    if (steady_iface.RTT < intermittent_iface.RTT) {
        int tmpsock = steady_csock;
        steady_csock = intermittent_csock;
        intermittent_csock = tmpsock;
    }
}

void
SpottyNetworkFailureTest::exchangeNetworkInterfaces(int bootstrap_sock)
{
    struct net_interface sentinel;
    memset(&sentinel, 0, sizeof(sentinel));
    int rc;
    struct CMMSocketControlHdr hdr;
    do {
        rc = read(bootstrap_sock, &hdr, sizeof(hdr));
        handle_error(rc != sizeof(hdr), "reading net iface");
    } while (memcmp(&hdr.op.new_interface, &sentinel, sizeof(struct net_interface)) != 0);

    struct CMMSocketControlHdr hdrs[2];
    memset(hdrs, 0, sizeof(hdrs));
    hdrs[0].type = htons(CMM_CONTROL_MSG_NEW_INTERFACE);
    inet_aton("141.212.110.132", &hdrs[0].op.new_interface.ip_addr);
    hdrs[0].op.new_interface.bandwidth_down = 1250000;
    hdrs[0].op.new_interface.bandwidth_up = 1250000;
    hdrs[0].op.new_interface.RTT = 1;
    hdrs[1].type = htons(CMM_CONTROL_MSG_NEW_INTERFACE);
    hdrs[0].op.new_interface = sentinel;
    rc = write(bootstrap_sock, hdrs, sizeof(hdrs));
    handle_error(rc != sizeof(hdrs), "sending net iface");
}

void
SpottyNetworkFailureTest::startSender()
{
    // create connecting multisocket
    EndToEndTestsBase::startSender();
}

void
SpottyNetworkFailureTest::tearDown()
{
    if (isReceiver()) {
        close(steady_csock);
        close(intermittent_csock);
        close(intnw_listen_sock);
        close(listen_sock);
    } else {
        EndToEndTestsBase::tearDown();
    }
}

int
connect_to_scout_control()
{
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    handle_error(sock < 0, "creating scout control socket");

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_port = htons(CONTROL_SOCKET_PORT);

    socklen_t addrlen = sizeof(addr);
    int rc = connect(sock, (struct sockaddr *)&addr, addrlen);
    handle_error(rc < 0, "connecting scout control socket");

    return sock;
}

void
SpottyNetworkFailureTest::testOneNetworkFails()
{
    const char expected_str[] = "ABCDEFGHIJ";
    const size_t len = strlen(expected_str);
    sleep(1);

    char buf[len + 1];
    memset(buf, 0, sizeof(buf));

    if (isReceiver()) {
        shutdown(intermittent_csock, SHUT_RDWR);

        // expected messages:
        //  in order:
        //  1) begin_irob
        //  2) irob_chunk
        //  3) end_irob
        //
        // at any time:
        //  - down_interface
        vector<struct CMMSocketControlHdr> hdrs;
        while (hdrs.size() < 4) {
            struct CMMSocketControlHdr hdr;
            memset(&hdr, 0, sizeof(hdr));
            int rc = recv(steady_csock, &hdr, sizeof(hdr), MSG_WAITALL);
            CPPUNIT_ASSERT_EQUAL((int)sizeof(hdr), rc);
            if (ntohs(hdr.type) == CMM_CONTROL_MSG_IROB_CHUNK) {
                CPPUNIT_ASSERT_EQUAL(len, ntohl(hdr.op.irob_chunk.datalen));

                rc = recv(steady_csock, buf, len, MSG_WAITALL);
                CPPUNIT_ASSERT_EQUAL((int)len, rc);
                CPPUNIT_ASSERT_EQUAL(string(expected_str), string(buf));
            }

            hdrs.push_back(hdr);
        }
        CPPUNIT_ASSERT_EQUAL(4U, hdrs.size());

        irob_id_t irob_id = -1;
        bool begin_irob_recvd = false;
        bool irob_chunk_recvd = false;
        bool end_irob_recvd = false;
        bool down_interface_recvd = false;
        for (size_t i = 0; i < hdrs.size(); ++i) {
            switch (ntohs(hdrs[i].type)) {
            case CMM_CONTROL_MSG_BEGIN_IROB:
                irob_id = ntohl(hdrs[i].op.begin_irob.id);
                begin_irob_recvd = true; break;
            case CMM_CONTROL_MSG_IROB_CHUNK:
                irob_chunk_recvd = true; break;
            case CMM_CONTROL_MSG_END_IROB:
                end_irob_recvd = true; break;
            case CMM_CONTROL_MSG_DOWN_INTERFACE:
                down_interface_recvd = true; break;
            default:
                CPPUNIT_ASSERT(false);
            }
        }
        CPPUNIT_ASSERT(begin_irob_recvd);
        CPPUNIT_ASSERT(irob_chunk_recvd);
        CPPUNIT_ASSERT(end_irob_recvd);
        CPPUNIT_ASSERT(down_interface_recvd);

        struct CMMSocketControlHdr ack;
        memset(&ack, 0, sizeof(ack));
        ack.type = htons(CMM_CONTROL_MSG_ACK);
        ack.op.ack.id = htonl(irob_id);
        int rc = write(steady_csock, &ack, sizeof(ack));
        CPPUNIT_ASSERT_EQUAL((int)sizeof(ack), rc);

        char response[sizeof(struct CMMSocketControlHdr) * 3 + len];
        memset(response, 0, sizeof(response));
        struct CMMSocketControlHdr *begin_irob_hdr = (struct CMMSocketControlHdr *) response;
        struct CMMSocketControlHdr *irob_chunk_hdr =
            (struct CMMSocketControlHdr *) (((char *) begin_irob_hdr) + sizeof(*begin_irob_hdr));
        char *resp_data = ((char *) irob_chunk_hdr) + sizeof(*irob_chunk_hdr);
        struct CMMSocketControlHdr *end_irob_hdr =
            (struct CMMSocketControlHdr *) (resp_data + len);

        irob_id_t resp_irob_id = irob_id + 1;
        begin_irob_hdr->type = htons(CMM_CONTROL_MSG_BEGIN_IROB);
        begin_irob_hdr->op.begin_irob.id = htonl(resp_irob_id);
        irob_chunk_hdr->op.irob_chunk.id = htonl(resp_irob_id);
        irob_chunk_hdr->op.irob_chunk.seqno = 0;
        irob_chunk_hdr->op.irob_chunk.datalen = htonl(len);
        memcpy(resp_data, expected_str, len);
        end_irob_hdr->op.end_irob.id = htonl(resp_irob_id);
        end_irob_hdr->op.end_irob.expected_bytes = htonl(len);
        end_irob_hdr->op.end_irob.expected_chunks = htonl(1);

        rc = write(steady_csock, response, sizeof(response));
        CPPUNIT_ASSERT_EQUAL((int) sizeof(response), rc);

        memset(&ack, 0, sizeof(ack));
        rc = recv(steady_csock, &ack, sizeof(ack), MSG_WAITALL);
        CPPUNIT_ASSERT_EQUAL((int) sizeof(ack), rc);
        CPPUNIT_ASSERT_EQUAL((uint16_t) CMM_CONTROL_MSG_ACK, ntohs(ack.type));
        CPPUNIT_ASSERT_EQUAL(resp_irob_id, (irob_id_t) ntohl(ack.op.ack.id));
    } else {
        int scout_control_sock = connect_to_scout_control();

        sleep(1);
        int rc = cmm_write(data_sock, expected_str, len, 0, NULL, NULL);
        CPPUNIT_ASSERT_EQUAL((int)len, rc); // succeeds immediately without waiting for bytes to be sent

        sleep(1);
        char cmd[] = "bg_down\n";
        rc = write(scout_control_sock, cmd, strlen(cmd));
        CPPUNIT_ASSERT_EQUAL((int) strlen(cmd), rc);

        sleep(1);
        memset(buf, 0, sizeof(buf));
        rc = cmm_recv(data_sock, buf, len, MSG_WAITALL, NULL);
        CPPUNIT_ASSERT_EQUAL((int) len, rc);
        CPPUNIT_ASSERT_EQUAL(string(expected_str), string(buf));

        close(scout_control_sock);
    }
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include <set>
#include <string>
#include <sstream>
#include <iterator>
#include <regex>
#include <chrono>

#include <unistd.h>
#include <sched.h>

#include "interference.h"
#include "interference_mpi.h"
#include "counter.hpp"
#include "perf.hpp"

#include "nlohmann/json.hpp"

using json = nlohmann::json;

std::string PREFIX;
std::string sched;
std::vector<int> affinity;
int localid;
bool mvapich_hack = false;

std::vector<int> parse_affinity(std::string cpu_string)
{
  std::vector<int> cpus;
  // static boost::regex r("(\\d+|\\d+-\\d+)?");
  std::regex r("^(\\d+-\\d+|\\d+)(,\\d+-\\d+|,\\d+)?(,\\d+-\\d+|,\\d+)*$");

  // Do regex match and convert the interesting part to
  // int.
  std::smatch what;
  if (!std::regex_match(cpu_string, what, r)) {
    throw std::runtime_error("Can't parse CPU string");
  }

  std::string::const_iterator start = cpu_string.begin();
  std::string::const_iterator end = cpu_string.end();
  while (std::regex_search(start, end, what, r)) {
    // what[1] single or a range of cpus
    if (!what[1].matched)
      throw std::runtime_error("Can't parse CPU string");

    std::string stest(what[1].first, what[1].second);
    auto minus = stest.find('-');
    if (minus == std::string::npos) {
      int value;
      try {
        value = std::stoi(stest);
      } catch (std::exception &e) {
        throw std::runtime_error("Can't parse CPU string");
      }
      cpus.push_back(value);
    } else {
      auto s = std::stoi(stest.substr(0, minus));
      auto e = std::stoi(stest.substr(minus+1));

      if (s > e)
        throw std::runtime_error("Can't parse CPU string");

      for (int cpu = s; cpu<=e; cpu++)
        cpus.push_back(cpu);
    }
    start = what[2].first;
    if (*start == ',')
      start++;
  }

  std::sort(cpus.begin(), cpus.end());
  cpus.erase(std::unique(cpus.begin(), cpus.end()), cpus.end());

  return cpus;
}

void parse_env()
{
  // Here we should set scheduler
  auto env = std::getenv("INTERFERENCE_PREFIX");
  if (!env)
    throw std::runtime_error("INTERFERENCE_PREFIX should be set");
  PREFIX = env;

  auto sched_ptr = std::getenv("INTERFERENCE_SCHED");
  if (!sched_ptr)
    throw std::runtime_error("INTERFERENCE_SCHED should be set");
  sched = std::string(sched_ptr);

  auto affinity_ptr = std::getenv("INTERFERENCE_AFFINITY");
  if (!affinity_ptr)
    throw std::runtime_error("INTERFERENCE_AFFINITY should be set");
  affinity = parse_affinity(affinity_ptr);

  auto localid_name_ptr = std::getenv("INTERFERENCE_LOCALID");
  if (localid_name_ptr) {
    auto localid_ptr = std::getenv(localid_name_ptr);
    if (!localid_ptr)
      throw std::runtime_error("INTERFERENCE_LOCALID points to nonexistent variable");
    localid = std::stol(localid_ptr);
  } else {
    // Probably this concept makes no sense in here
    localid = 0;
  }

  if (std::getenv("INTERFERENCE_HACK"))
    mvapich_hack = true;
}

/**
 *  Set affinity to a particular cpu
 */
static void set_own_affinity(const cpu_set_t &cpu_set)
{
  int ret = sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
  if (ret)
    throw std::runtime_error("Failed to set affinity");
}

static void set_own_affinity(int cpu)
{
  cpu_set_t cpu_set;

  CPU_ZERO(&cpu_set);
  CPU_SET(cpu, &cpu_set);
  set_own_affinity(cpu_set);
}

static void set_own_affinity(const std::vector<int> &cpus)
{
  cpu_set_t cpu_set;

  CPU_ZERO(&cpu_set);
  for (auto c : cpus)
    CPU_SET(c, &cpu_set);
  set_own_affinity(cpu_set);
}

class WallClock : public IntervalCounter<wall_time_t> {
public:
  using IntervalCounter<wall_time_t>::IntervalCounter;

  void get_value(wall_time_t &val) {
    val = std::chrono::system_clock::now();
  }

  void exchange() {
    auto diff = end - start;
    long diff_long = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();

    _values.resize(_ranks);
    gather(&diff_long, sizeof(diff_long), _values.data());
  }
};

class ProcReader : public IntervalCounter<milli_time_t> {
  std::vector<std::string> get_stat_strings()
  {
    std::ifstream proc("/proc/self/stat");

    std::string line;
    std::getline(proc, line);
    std::istringstream iss(line);

    std::vector<std::string> tokens;
    std::copy(std::istream_iterator<std::string>(iss),
              std::istream_iterator<std::string>(),
              std::back_inserter(tokens));

    return tokens;
  }

public:
  using IntervalCounter<milli_time_t>::IntervalCounter;

  void get_value(milli_time_t &val) {
    std::vector<std::string> tokens = get_stat_strings();

    val = milli_time_t(std::stol(tokens[13]) * 1000 / sysconf(_SC_CLK_TCK));
  }

  void exchange() {
    auto diff = end - start;
    long diff_long = diff.count();
    _values.resize(_ranks);
    gather(&diff_long, sizeof(diff_long), _values.data());
  }
};

class CPUaccounter : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    if (sched == "pinned") {
      _value = affinity[localid % affinity.size()];
      set_own_affinity(_value);
    } else if (sched == "cfs") {
      _value = -1;
      set_own_affinity(affinity);
    }
  }
};

class LocalId : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    _value = localid;
  }
};

class HostNameAccounter : public Counter {
protected:
  std::vector<char> _value;

  int _ranks;
  std::vector<char> _values;
  const unsigned name_len;
public:
  HostNameAccounter(int ranks, std::string name, unsigned name_len = 20) :
    Counter(name),
    _ranks(ranks),
    name_len(name_len)
  {
  }

  CounterMap emit() {
    CounterMap map;
    std::vector<std::string> str_values;

    for (int i = 0; i < _ranks; i ++) {
      auto name = std::string(_values.data() + i * name_len);
      str_values.push_back(name);
    }

    map[_name] = str_values;
    return map;
  }

  void end_accounting() {
    exchange();
  }

  void start_accounting() {
    // Assume no overflow happens
    _value.resize(name_len);
    gethostname(_value.data(), name_len);
    _value[name_len - 1] = '\0';
  }

private:
  void exchange() {
    _values.resize(name_len * _ranks);
    gather_names(_value.data(), _values.data(), name_len);
  }

};

class IterAccounter : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    _value = 1;
  }
};

class InterferenceAccounter : public Accounter {
  const std::string output_format;

private:
  typedef std::unique_ptr<Counter> counter_ptr;

  std::vector<std::string> parse_perf_counters(const std::string &str) {
    std::vector<std::string> result;
    std::stringstream ss(str);
    std::string token;

    while(std::getline(ss, token, ',')) {
      result.push_back(token);
    }
  }

  void parse_and_add_perf_counters(const std::string &str) {
    std::vector<std::string> counter_list;
    counter_list = parse_perf_counters(str);

    if (counter_list.size() == 0)
      return;

    for (const auto &cnt : counter_list) {
      _counters.push_back(counter_ptr(new PerfCounter(_ranks, cnt)));
    }
  }

public:
  InterferenceAccounter(int ranks, const std::string &output_format) :
    Accounter(ranks), output_format(output_format)
  {
    _counters.push_back(counter_ptr(new IterAccounter(ranks, "ITER")));
    _counters.push_back(counter_ptr(new HostNameAccounter(ranks, "NODE")));
    _counters.push_back(counter_ptr(new LocalId(ranks, "LOCALID")));
    _counters.push_back(counter_ptr(new CPUaccounter(ranks, "CPU")));
    _counters.push_back(counter_ptr(new WallClock(ranks, "WTIME")));
    _counters.push_back(counter_ptr(new ProcReader(ranks, "UTIME")));

    if (output_format != "csv" && output_format != "json") {
      throw std::runtime_error("Unknown output format: " + output_format);
    }

    // Allow additional perf counters for json
    if (output_format == "json") {
      auto counters = std::getenv("INTERFERENCE_PERF");
      if (counters) {
        parse_and_add_perf_counters(counters);
      }
    }
  }

  void dump_csv(const CounterMap &map) {
    for (int i = 0; i < _ranks; i++) {
      std::cout << PREFIX
                << " ,RANK: " << i;
      for (const auto &cnt : map) {
        std::cout << " ," << cnt.first << ": "
                  << cnt.second[i];
      }
      std::cout << std::endl;
    }
  }

  void dump_json(const CounterMap &map) {
    json j;

    for (int i = 0; i < _ranks; i++) {
      json row;
      for (const auto &cnt : map) {
        row[cnt.first] = cnt.second[i];
      }
      j[PREFIX].push_back(row);
    }

    std::cout << j.dump() << std::endl;
  }

  void dump(const std::set<std::string> &filter = std::set<std::string>()) {
    int my_rank = get_my_rank();
    if (my_rank != 0)
      return;

    auto map = generate_map(filter);

    if (output_format == "csv") {
      dump_csv(map);
    } else if (output_format == "json") {
      dump_json(map);
    }
  }
};

std::unique_ptr<InterferenceAccounter> accounter;

void interference_start()
{
  parse_env();

  int ranks = get_ranks();

  accounter = std::unique_ptr<InterferenceAccounter>(
    new InterferenceAccounter(ranks, std::getenv("INTERFERENCE_OUTPUT")));
  accounter->start_accounting();
}

void interference_end()
{
  // Here we should read stat
  accounter->end_accounting();

  accounter->dump();

  if (mvapich_hack) {
    barrier();
    std::flush(std::cout);
    exit(0);
  }
}
<commit_msg>Fix tokenization bug<commit_after>#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include <set>
#include <string>
#include <sstream>
#include <iterator>
#include <regex>
#include <chrono>

#include <unistd.h>
#include <sched.h>

#include "interference.h"
#include "interference_mpi.h"
#include "counter.hpp"
#include "perf.hpp"

#include "nlohmann/json.hpp"

using json = nlohmann::json;

std::string PREFIX;
std::string sched;
std::vector<int> affinity;
int localid;
bool mvapich_hack = false;

std::vector<int> parse_affinity(std::string cpu_string)
{
  std::vector<int> cpus;
  // static boost::regex r("(\\d+|\\d+-\\d+)?");
  std::regex r("^(\\d+-\\d+|\\d+)(,\\d+-\\d+|,\\d+)?(,\\d+-\\d+|,\\d+)*$");

  // Do regex match and convert the interesting part to
  // int.
  std::smatch what;
  if (!std::regex_match(cpu_string, what, r)) {
    throw std::runtime_error("Can't parse CPU string");
  }

  std::string::const_iterator start = cpu_string.begin();
  std::string::const_iterator end = cpu_string.end();
  while (std::regex_search(start, end, what, r)) {
    // what[1] single or a range of cpus
    if (!what[1].matched)
      throw std::runtime_error("Can't parse CPU string");

    std::string stest(what[1].first, what[1].second);
    auto minus = stest.find('-');
    if (minus == std::string::npos) {
      int value;
      try {
        value = std::stoi(stest);
      } catch (std::exception &e) {
        throw std::runtime_error("Can't parse CPU string");
      }
      cpus.push_back(value);
    } else {
      auto s = std::stoi(stest.substr(0, minus));
      auto e = std::stoi(stest.substr(minus+1));

      if (s > e)
        throw std::runtime_error("Can't parse CPU string");

      for (int cpu = s; cpu<=e; cpu++)
        cpus.push_back(cpu);
    }
    start = what[2].first;
    if (*start == ',')
      start++;
  }

  std::sort(cpus.begin(), cpus.end());
  cpus.erase(std::unique(cpus.begin(), cpus.end()), cpus.end());

  return cpus;
}

void parse_env()
{
  // Here we should set scheduler
  auto env = std::getenv("INTERFERENCE_PREFIX");
  if (!env)
    throw std::runtime_error("INTERFERENCE_PREFIX should be set");
  PREFIX = env;

  auto sched_ptr = std::getenv("INTERFERENCE_SCHED");
  if (!sched_ptr)
    throw std::runtime_error("INTERFERENCE_SCHED should be set");
  sched = std::string(sched_ptr);

  auto affinity_ptr = std::getenv("INTERFERENCE_AFFINITY");
  if (!affinity_ptr)
    throw std::runtime_error("INTERFERENCE_AFFINITY should be set");
  affinity = parse_affinity(affinity_ptr);

  auto localid_name_ptr = std::getenv("INTERFERENCE_LOCALID");
  if (localid_name_ptr) {
    auto localid_ptr = std::getenv(localid_name_ptr);
    if (!localid_ptr)
      throw std::runtime_error("INTERFERENCE_LOCALID points to nonexistent variable");
    localid = std::stol(localid_ptr);
  } else {
    // Probably this concept makes no sense in here
    localid = 0;
  }

  if (std::getenv("INTERFERENCE_HACK"))
    mvapich_hack = true;
}

/**
 *  Set affinity to a particular cpu
 */
static void set_own_affinity(const cpu_set_t &cpu_set)
{
  int ret = sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
  if (ret)
    throw std::runtime_error("Failed to set affinity");
}

static void set_own_affinity(int cpu)
{
  cpu_set_t cpu_set;

  CPU_ZERO(&cpu_set);
  CPU_SET(cpu, &cpu_set);
  set_own_affinity(cpu_set);
}

static void set_own_affinity(const std::vector<int> &cpus)
{
  cpu_set_t cpu_set;

  CPU_ZERO(&cpu_set);
  for (auto c : cpus)
    CPU_SET(c, &cpu_set);
  set_own_affinity(cpu_set);
}

class WallClock : public IntervalCounter<wall_time_t> {
public:
  using IntervalCounter<wall_time_t>::IntervalCounter;

  void get_value(wall_time_t &val) {
    val = std::chrono::system_clock::now();
  }

  void exchange() {
    auto diff = end - start;
    long diff_long = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();

    _values.resize(_ranks);
    gather(&diff_long, sizeof(diff_long), _values.data());
  }
};

class ProcReader : public IntervalCounter<milli_time_t> {
  std::vector<std::string> get_stat_strings()
  {
    std::ifstream proc("/proc/self/stat");

    std::string line;
    std::getline(proc, line);
    std::istringstream iss(line);

    std::vector<std::string> tokens;
    std::copy(std::istream_iterator<std::string>(iss),
              std::istream_iterator<std::string>(),
              std::back_inserter(tokens));

    return tokens;
  }

public:
  using IntervalCounter<milli_time_t>::IntervalCounter;

  void get_value(milli_time_t &val) {
    std::vector<std::string> tokens = get_stat_strings();

    val = milli_time_t(std::stol(tokens[13]) * 1000 / sysconf(_SC_CLK_TCK));
  }

  void exchange() {
    auto diff = end - start;
    long diff_long = diff.count();
    _values.resize(_ranks);
    gather(&diff_long, sizeof(diff_long), _values.data());
  }
};

class CPUaccounter : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    if (sched == "pinned") {
      _value = affinity[localid % affinity.size()];
      set_own_affinity(_value);
    } else if (sched == "cfs") {
      _value = -1;
      set_own_affinity(affinity);
    }
  }
};

class LocalId : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    _value = localid;
  }
};

class HostNameAccounter : public Counter {
protected:
  std::vector<char> _value;

  int _ranks;
  std::vector<char> _values;
  const unsigned name_len;
public:
  HostNameAccounter(int ranks, std::string name, unsigned name_len = 20) :
    Counter(name),
    _ranks(ranks),
    name_len(name_len)
  {
  }

  CounterMap emit() {
    CounterMap map;
    std::vector<std::string> str_values;

    for (int i = 0; i < _ranks; i ++) {
      auto name = std::string(_values.data() + i * name_len);
      str_values.push_back(name);
    }

    map[_name] = str_values;
    return map;
  }

  void end_accounting() {
    exchange();
  }

  void start_accounting() {
    // Assume no overflow happens
    _value.resize(name_len);
    gethostname(_value.data(), name_len);
    _value[name_len - 1] = '\0';
  }

private:
  void exchange() {
    _values.resize(name_len * _ranks);
    gather_names(_value.data(), _values.data(), name_len);
  }

};

class IterAccounter : public SingleCounter<long> {
public:
  using SingleCounter<long>::SingleCounter;

  void start_accounting() {
    _value = 1;
  }
};

class InterferenceAccounter : public Accounter {
  const std::string output_format;

private:
  typedef std::unique_ptr<Counter> counter_ptr;

  std::vector<std::string> parse_perf_counters(const std::string &str) {
    std::vector<std::string> result;
    std::stringstream ss(str);
    std::string token;

    while(std::getline(ss, token, ',')) {
      result.push_back(token);
    }

    return result;
  }

  void parse_and_add_perf_counters(const std::string &str) {
    std::vector<std::string> counter_list;
    counter_list = parse_perf_counters(str);

    if (counter_list.size() == 0)
      return;

    for (const auto &cnt : counter_list) {
      _counters.push_back(counter_ptr(new PerfCounter(_ranks, cnt)));
    }
  }

public:
  InterferenceAccounter(int ranks, const std::string &output_format) :
    Accounter(ranks), output_format(output_format)
  {
    _counters.push_back(counter_ptr(new IterAccounter(ranks, "ITER")));
    _counters.push_back(counter_ptr(new HostNameAccounter(ranks, "NODE")));
    _counters.push_back(counter_ptr(new LocalId(ranks, "LOCALID")));
    _counters.push_back(counter_ptr(new CPUaccounter(ranks, "CPU")));
    _counters.push_back(counter_ptr(new WallClock(ranks, "WTIME")));
    _counters.push_back(counter_ptr(new ProcReader(ranks, "UTIME")));

    if (output_format != "csv" && output_format != "json") {
      throw std::runtime_error("Unknown output format: " + output_format);
    }

    // Allow additional perf counters for json
    if (output_format == "json") {
      auto counters = std::getenv("INTERFERENCE_PERF");
      if (counters) {
        parse_and_add_perf_counters(counters);
      }
    }
  }

  void dump_csv(const CounterMap &map) {
    for (int i = 0; i < _ranks; i++) {
      std::cout << PREFIX
                << " ,RANK: " << i;
      for (const auto &cnt : map) {
        std::cout << " ," << cnt.first << ": "
                  << cnt.second[i];
      }
      std::cout << std::endl;
    }
  }

  void dump_json(const CounterMap &map) {
    json j;

    for (int i = 0; i < _ranks; i++) {
      json row;
      for (const auto &cnt : map) {
        row[cnt.first] = cnt.second[i];
      }
      j[PREFIX].push_back(row);
    }

    std::cout << j.dump() << std::endl;
  }

  void dump(const std::set<std::string> &filter = std::set<std::string>()) {
    int my_rank = get_my_rank();
    if (my_rank != 0)
      return;

    auto map = generate_map(filter);

    if (output_format == "csv") {
      dump_csv(map);
    } else if (output_format == "json") {
      dump_json(map);
    }
  }
};

std::unique_ptr<InterferenceAccounter> accounter;

void interference_start()
{
  parse_env();

  int ranks = get_ranks();

  accounter = std::unique_ptr<InterferenceAccounter>(
    new InterferenceAccounter(ranks, std::getenv("INTERFERENCE_OUTPUT")));
  accounter->start_accounting();
}

void interference_end()
{
  // Here we should read stat
  accounter->end_accounting();

  accounter->dump();

  if (mvapich_hack) {
    barrier();
    std::flush(std::cout);
    exit(0);
  }
}
<|endoftext|>