<commit_before>#include <stdio.h>
#include <Halide.h>

// Test the simplifier in Halide by testing for equivalence of randomly generated expressions.

using namespace std;
using namespace Halide;
using namespace Halide::Internal;

const int fuzz_var_count = 5;

Type fuzz_types[] = { UInt(1), UInt(8), UInt(16), UInt(32), Int(8), Int(16), Int(32) };
const int fuzz_type_count = sizeof(fuzz_types)/sizeof(fuzz_types[0]);

std::string fuzz_var(int i) {
    return std::string(1, 'a' + i);
}

Expr random_var() {
    return Variable::make(Int(0), fuzz_var(rand()%fuzz_var_count));
}

Type random_type(int width) {
    Type T = fuzz_types[rand()%fuzz_type_count];
    if (width > 1) {
        T = T.vector_of(width);
    }
    return T;
}

Expr random_leaf(Type T, bool imm_only = false) {
    if (T.is_scalar()) {
        int var = rand()%fuzz_var_count + 1;
        if (!imm_only && var < fuzz_var_count) {
            return cast(T, random_var());
        } else {
            if (T == Int(32)) {
                // For Int(32), we don't care about correctness during
                // overflow, so just use numbers that are unlikely to
                // overflow.
                return cast(T, rand()%256 - 128);
            } else {
                return cast(T, rand() - RAND_MAX/2);
            }
        }
    } else {
        if (rand() % 2 == 0) {
            return Ramp::make(random_leaf(T.element_of()), random_leaf(T.element_of()), T.width);
        } else {
            return Broadcast::make(random_leaf(T.element_of()), T.width);
        }
    }
}

Expr random_expr(Type T, int depth);

Expr random_condition(Type T, int depth) {
    typedef Expr (*make_bin_op_fn)(Expr, Expr);
    static make_bin_op_fn make_bin_op[] = {
        EQ::make,
        NE::make,
        LT::make,
        LE::make,
        GT::make,
        GE::make,
    };
    const int op_count = sizeof(make_bin_op)/sizeof(make_bin_op[0]);

    Expr a = random_expr(T, depth);
    Expr b = random_expr(T, depth);
    int op = rand()%op_count;
    return make_bin_op[op](a, b);
}

Expr random_expr(Type T, int depth) {
    typedef Expr (*make_bin_op_fn)(Expr, Expr);
    static make_bin_op_fn make_bin_op[] = {
        Add::make,
        Sub::make,
        Mul::make,
        Min::make,
        Max::make,
        Div::make,
        Mod::make,
     };

    static make_bin_op_fn make_bool_bin_op[] = {
        And::make,
        Or::make,
    };

    if (depth-- <= 0) {
        return random_leaf(T);
    }

    const int bin_op_count = sizeof(make_bin_op) / sizeof(make_bin_op[0]);
    const int bool_bin_op_count = sizeof(make_bool_bin_op) / sizeof(make_bool_bin_op[0]);
    const int op_count = bin_op_count + bool_bin_op_count + 5;

    int op = rand() % op_count;
    switch(op) {
    case 0: return random_leaf(T);
    case 1: return Select::make(random_condition(T, depth),
                                random_expr(T, depth),
                                random_expr(T, depth));

    case 2:
        if (T.width != 1) {
            return Broadcast::make(random_expr(T.element_of(), depth),
                                   T.width);
        }
        break;
    case 3:
        if (T.width != 1) {
            return Ramp::make(random_expr(T.element_of(), depth),
                              random_expr(T.element_of(), depth),
                              T.width);
        }
        break;

    case 4:
        if (T.is_bool()) {
            return Not::make(random_expr(T, depth));
        }
        break;

    case 5:
        // When generating boolean expressions, maybe throw in a condition on non-bool types.
        if (T.is_bool()) {
            return random_condition(T, depth);
        }
        break;

    case 6:
        // Get a random type that isn't T or int32 (int32 can overflow and we don't care about that).
        Type subT;
        do {
            subT = random_type(T.width);
        } while (subT == T || (subT.is_int() && subT.bits == 32));
        return Cast::make(T, random_expr(subT, depth));

    default:
        make_bin_op_fn maker;
        if (T.is_bool()) {
            maker = make_bool_bin_op[op%bool_bin_op_count];
        } else {
            maker = make_bin_op[op%bin_op_count];
        }
        Expr a = random_expr(T, depth);
        Expr b = random_expr(T, depth);
        return maker(a, b);
    }
    // If we got here, try again.
    return random_expr(T, depth);
}

bool test_simplification(Expr a, Expr b, Type T, const map<string, Expr> &vars) {
    for (int j = 0; j < T.width; j++) {
        Expr a_j = a;
        Expr b_j = b;
        if (T.width != 1) {
            a_j = extract_lane(a, j);
            b_j = extract_lane(b, j);
        }

        Expr a_j_v = simplify(substitute(vars, a_j));
        Expr b_j_v = simplify(substitute(vars, b_j));
        // If the simplifier didn't produce constants, there must be
        // undefined behavior in this expression. Ignore it.
        if (!is_const(a_j_v) || !is_const(b_j_v)) {
            continue;
        }
        if (!equal(a_j_v, b_j_v)) {
            for(map<string, Expr>::const_iterator i = vars.begin(); i != vars.end(); i++) {
                std::cout << i->first << " = " << i->second << '\n';
            }

            std::cout << a << '\n';
            std::cout << b << '\n';
            std::cout << "In vector lane " << j << ":\n";
            std::cout << a_j << " -> " << a_j_v << '\n';
            std::cout << b_j << " -> " << b_j_v << '\n';
            return false;
        }
    }
    return true;
}

bool test_expression(Expr test, int samples) {
    Expr simplified = simplify(test);

    map<string, Expr> vars;
    for (int i = 0; i < fuzz_var_count; i++) {
        vars[fuzz_var(i)] = Expr();
    }

    for (int i = 0; i < samples; i++) {
        for (std::map<string, Expr>::iterator v = vars.begin(); v != vars.end(); v++) {
            v->second = random_leaf(test.type().element_of(), true);
        }

        if (!test_simplification(test, simplified, test.type(), vars)) {
            return false;
        }
    }
    return true;
}

Expr ramp(Expr b, Expr s, int w) { return Ramp::make(b, s, w); }
Expr x1(Expr x) { return Broadcast::make(x, 2); }
Expr x2(Expr x) { return Broadcast::make(x, 2); }
Expr x4(Expr x) { return Broadcast::make(x, 2); }
Expr uint1(Expr x) { return Cast::make(UInt(1), x); }
Expr uint8(Expr x) { return Cast::make(UInt(8), x); }
Expr uint16(Expr x) { return Cast::make(UInt(16), x); }
Expr uint32(Expr x) { return Cast::make(UInt(32), x); }
Expr int8(Expr x) { return Cast::make(Int(8), x); }
Expr int16(Expr x) { return Cast::make(Int(16), x); }
Expr int32(Expr x) { return Cast::make(Int(32), x); }
Expr uint1x2(Expr x) { return Cast::make(UInt(1).vector_of(2), x); }
Expr uint8x2(Expr x) { return Cast::make(UInt(8).vector_of(2), x); }
Expr uint16x2(Expr x) { return Cast::make(UInt(16).vector_of(2), x); }
Expr uint32x2(Expr x) { return Cast::make(UInt(32).vector_of(2), x); }
Expr int8x2(Expr x) { return Cast::make(Int(8).vector_of(2), x); }
Expr int16x2(Expr x) { return Cast::make(Int(16).vector_of(2), x); }
Expr int32x2(Expr x) { return Cast::make(Int(32).vector_of(2), x); }

Expr a(Variable::make(Int(0), fuzz_var(0)));
Expr b(Variable::make(Int(0), fuzz_var(1)));
Expr c(Variable::make(Int(0), fuzz_var(2)));
Expr d(Variable::make(Int(0), fuzz_var(3)));
Expr e(Variable::make(Int(0), fuzz_var(4)));

int main(int argc, char **argv) {
    // Number of random expressions to test.
    const int count = 1000;
    // Depth of the randomly generated expression trees.
    const int depth = 5;
    // Number of samples to test the generated expressions for.
    const int samples = 3;

    // We want different fuzz tests every time, to increase coverage.
    // We also report the seed to enable reproducing failures.
    int fuzz_seed = time(NULL);
    srand(fuzz_seed);
    std::cout << "Simplify fuzz test seed: " << fuzz_seed << '\n';

    int max_fuzz_vector_width = 4;

    for (size_t i = 0; i < fuzz_type_count; i++) {
        Type T = fuzz_types[i];
        for (int w = 1; w < max_fuzz_vector_width; w *= 2) {
            Type VT = T.vector_of(w);
            for (int n = 0; n < count; n++) {
                // Generate a random expr...
                Expr test = random_expr(VT, depth);
                if (!test_expression(test, samples)) {
                    return -1;
                }
            }
        }
    }
    std::cout << "Success!" << std::endl;
    return 0;
}

<commit_msg>Fix ambiguous symbol resolution with libc++<commit_after>#include <stdio.h>
#include <Halide.h>

// Test the simplifier in Halide by testing for equivalence of randomly generated expressions.

using namespace std;
using namespace Halide;
using namespace Halide::Internal;

const int fuzz_var_count = 5;

Type fuzz_types[] = { UInt(1), UInt(8), UInt(16), UInt(32), Int(8), Int(16), Int(32) };
const int fuzz_type_count = sizeof(fuzz_types)/sizeof(fuzz_types[0]);

std::string fuzz_var(int i) {
    return std::string(1, 'a' + i);
}

Expr random_var() {
    return Variable::make(Int(0), fuzz_var(rand()%fuzz_var_count));
}

Type random_type(int width) {
    Type T = fuzz_types[rand()%fuzz_type_count];
    if (width > 1) {
        T = T.vector_of(width);
    }
    return T;
}

Expr random_leaf(Type T, bool imm_only = false) {
    if (T.is_scalar()) {
        int var = rand()%fuzz_var_count + 1;
        if (!imm_only && var < fuzz_var_count) {
            return cast(T, random_var());
        } else {
            if (T == Int(32)) {
                // For Int(32), we don't care about correctness during
                // overflow, so just use numbers that are unlikely to
                // overflow.
                return cast(T, rand()%256 - 128);
            } else {
                return cast(T, rand() - RAND_MAX/2);
            }
        }
    } else {
        if (rand() % 2 == 0) {
            return Ramp::make(random_leaf(T.element_of()), random_leaf(T.element_of()), T.width);
        } else {
            return Broadcast::make(random_leaf(T.element_of()), T.width);
        }
    }
}

Expr random_expr(Type T, int depth);

Expr random_condition(Type T, int depth) {
    typedef Expr (*make_bin_op_fn)(Expr, Expr);
    static make_bin_op_fn make_bin_op[] = {
        EQ::make,
        NE::make,
        LT::make,
        LE::make,
        GT::make,
        GE::make,
    };
    const int op_count = sizeof(make_bin_op)/sizeof(make_bin_op[0]);

    Expr a = random_expr(T, depth);
    Expr b = random_expr(T, depth);
    int op = rand()%op_count;
    return make_bin_op[op](a, b);
}

Expr random_expr(Type T, int depth) {
    typedef Expr (*make_bin_op_fn)(Expr, Expr);
    static make_bin_op_fn make_bin_op[] = {
        Add::make,
        Sub::make,
        Mul::make,
        Min::make,
        Max::make,
        Div::make,
        Mod::make,
     };

    static make_bin_op_fn make_bool_bin_op[] = {
        And::make,
        Or::make,
    };

    if (depth-- <= 0) {
        return random_leaf(T);
    }

    const int bin_op_count = sizeof(make_bin_op) / sizeof(make_bin_op[0]);
    const int bool_bin_op_count = sizeof(make_bool_bin_op) / sizeof(make_bool_bin_op[0]);
    const int op_count = bin_op_count + bool_bin_op_count + 5;

    int op = rand() % op_count;
    switch(op) {
    case 0: return random_leaf(T);
    case 1: return Select::make(random_condition(T, depth),
                                random_expr(T, depth),
                                random_expr(T, depth));

    case 2:
        if (T.width != 1) {
            return Broadcast::make(random_expr(T.element_of(), depth),
                                   T.width);
        }
        break;
    case 3:
        if (T.width != 1) {
            return Ramp::make(random_expr(T.element_of(), depth),
                              random_expr(T.element_of(), depth),
                              T.width);
        }
        break;

    case 4:
        if (T.is_bool()) {
            return Not::make(random_expr(T, depth));
        }
        break;

    case 5:
        // When generating boolean expressions, maybe throw in a condition on non-bool types.
        if (T.is_bool()) {
            return random_condition(T, depth);
        }
        break;

    case 6:
        // Get a random type that isn't T or int32 (int32 can overflow and we don't care about that).
        Type subT;
        do {
            subT = random_type(T.width);
        } while (subT == T || (subT.is_int() && subT.bits == 32));
        return Cast::make(T, random_expr(subT, depth));

    default:
        make_bin_op_fn maker;
        if (T.is_bool()) {
            maker = make_bool_bin_op[op%bool_bin_op_count];
        } else {
            maker = make_bin_op[op%bin_op_count];
        }
        Expr a = random_expr(T, depth);
        Expr b = random_expr(T, depth);
        return maker(a, b);
    }
    // If we got here, try again.
    return random_expr(T, depth);
}

bool test_simplification(Expr a, Expr b, Type T, const map<string, Expr> &vars) {
    for (int j = 0; j < T.width; j++) {
        Expr a_j = a;
        Expr b_j = b;
        if (T.width != 1) {
            a_j = extract_lane(a, j);
            b_j = extract_lane(b, j);
        }

        Expr a_j_v = simplify(substitute(vars, a_j));
        Expr b_j_v = simplify(substitute(vars, b_j));
        // If the simplifier didn't produce constants, there must be
        // undefined behavior in this expression. Ignore it.
        if (!Internal::is_const(a_j_v) || !Internal::is_const(b_j_v)) {
            continue;
        }
        if (!equal(a_j_v, b_j_v)) {
            for(map<string, Expr>::const_iterator i = vars.begin(); i != vars.end(); i++) {
                std::cout << i->first << " = " << i->second << '\n';
            }

            std::cout << a << '\n';
            std::cout << b << '\n';
            std::cout << "In vector lane " << j << ":\n";
            std::cout << a_j << " -> " << a_j_v << '\n';
            std::cout << b_j << " -> " << b_j_v << '\n';
            return false;
        }
    }
    return true;
}

bool test_expression(Expr test, int samples) {
    Expr simplified = simplify(test);

    map<string, Expr> vars;
    for (int i = 0; i < fuzz_var_count; i++) {
        vars[fuzz_var(i)] = Expr();
    }

    for (int i = 0; i < samples; i++) {
        for (std::map<string, Expr>::iterator v = vars.begin(); v != vars.end(); v++) {
            v->second = random_leaf(test.type().element_of(), true);
        }

        if (!test_simplification(test, simplified, test.type(), vars)) {
            return false;
        }
    }
    return true;
}

Expr ramp(Expr b, Expr s, int w) { return Ramp::make(b, s, w); }
Expr x1(Expr x) { return Broadcast::make(x, 2); }
Expr x2(Expr x) { return Broadcast::make(x, 2); }
Expr x4(Expr x) { return Broadcast::make(x, 2); }
Expr uint1(Expr x) { return Cast::make(UInt(1), x); }
Expr uint8(Expr x) { return Cast::make(UInt(8), x); }
Expr uint16(Expr x) { return Cast::make(UInt(16), x); }
Expr uint32(Expr x) { return Cast::make(UInt(32), x); }
Expr int8(Expr x) { return Cast::make(Int(8), x); }
Expr int16(Expr x) { return Cast::make(Int(16), x); }
Expr int32(Expr x) { return Cast::make(Int(32), x); }
Expr uint1x2(Expr x) { return Cast::make(UInt(1).vector_of(2), x); }
Expr uint8x2(Expr x) { return Cast::make(UInt(8).vector_of(2), x); }
Expr uint16x2(Expr x) { return Cast::make(UInt(16).vector_of(2), x); }
Expr uint32x2(Expr x) { return Cast::make(UInt(32).vector_of(2), x); }
Expr int8x2(Expr x) { return Cast::make(Int(8).vector_of(2), x); }
Expr int16x2(Expr x) { return Cast::make(Int(16).vector_of(2), x); }
Expr int32x2(Expr x) { return Cast::make(Int(32).vector_of(2), x); }

Expr a(Variable::make(Int(0), fuzz_var(0)));
Expr b(Variable::make(Int(0), fuzz_var(1)));
Expr c(Variable::make(Int(0), fuzz_var(2)));
Expr d(Variable::make(Int(0), fuzz_var(3)));
Expr e(Variable::make(Int(0), fuzz_var(4)));

int main(int argc, char **argv) {
    // Number of random expressions to test.
    const int count = 1000;
    // Depth of the randomly generated expression trees.
    const int depth = 5;
    // Number of samples to test the generated expressions for.
    const int samples = 3;

    // We want different fuzz tests every time, to increase coverage.
    // We also report the seed to enable reproducing failures.
    int fuzz_seed = time(NULL);
    srand(fuzz_seed);
    std::cout << "Simplify fuzz test seed: " << fuzz_seed << '\n';

    int max_fuzz_vector_width = 4;

    for (size_t i = 0; i < fuzz_type_count; i++) {
        Type T = fuzz_types[i];
        for (int w = 1; w < max_fuzz_vector_width; w *= 2) {
            Type VT = T.vector_of(w);
            for (int n = 0; n < count; n++) {
                // Generate a random expr...
                Expr test = random_expr(VT, depth);
                if (!test_expression(test, samples)) {
                    return -1;
                }
            }
        }
    }
    std::cout << "Success!" << std::endl;
    return 0;
}

<|endoftext|>


<commit_before>#include "stdafx.h"
#include "AutowiringBenchmarkTest.h"
#include "TestFixtures/SimpleObject.h"
#include <boost/chrono/system_clocks.hpp>

TEST_F(AutowiringBenchmarkTest, VerifySimplePerformance) {
  const size_t n = 10000;

  // Insert the object:
  AutoRequired<SimpleObject>();

  // Time n hash map hits, in order to get a baseline:
  std::unordered_map<int, int> ref;
  ref[0] = 212;
  ref[1] = 21;

  boost::chrono::nanoseconds baseline;
  {
    auto start = boost::chrono::high_resolution_clock::now();
    for(size_t i = n; i--;)
      ref[i % 2];
    baseline = boost::chrono::high_resolution_clock::now() - start;
  }

  // Time n autowirings:
  boost::chrono::nanoseconds benchmark;
  {
    auto start = boost::chrono::high_resolution_clock::now();
    for(size_t i = n; i--;)
      Autowired<SimpleObject>();
    benchmark = (boost::chrono::high_resolution_clock::now() - start) / n;
  }

  EXPECT_GT(baseline * 3. / 2., benchmark) << "Average time to autowire one member was more than 3/2 of a hash map lookup";
}

template<int N>
struct dummy:
  public virtual ContextMember
{};

TEST_F(AutowiringBenchmarkTest, VerifyAutowiringCache) {
  boost::chrono::nanoseconds baseline(0);
  boost::chrono::nanoseconds benchmark(0);

  auto inj = [] {
    Autowired<dummy<1>>();
    Autowired<dummy<2>>();
    Autowired<dummy<3>>();
    Autowired<dummy<4>>();
    Autowired<dummy<5>>();
    Autowired<dummy<6>>();
    Autowired<dummy<7>>();
    Autowired<dummy<8>>();
    Autowired<dummy<9>>();
    Autowired<dummy<10>>();
    Autowired<dummy<11>>();
    Autowired<dummy<12>>();
    Autowired<dummy<13>>();
    Autowired<dummy<14>>();
    Autowired<dummy<15>>();
    Autowired<dummy<16>>();
    Autowired<dummy<17>>();
    Autowired<dummy<18>>();
    Autowired<dummy<19>>();
    Autowired<dummy<20>>();
    Autowired<dummy<21>>();
    Autowired<dummy<22>>();
    Autowired<dummy<23>>();
    Autowired<dummy<24>>();
    Autowired<dummy<25>>();
  };

  for(int i = 0; i<100; ++i) {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    auto startBase = boost::chrono::high_resolution_clock::now();
    inj();
    baseline += boost::chrono::high_resolution_clock::now() - startBase;

    auto startBench = boost::chrono::high_resolution_clock::now();
    inj();
    benchmark += boost::chrono::high_resolution_clock::now() - startBench;
  }

  EXPECT_GT(baseline, benchmark) << "Autowiring cache not improving performance on subsequent autowirings";
}

TEST_F(AutowiringBenchmarkTest, VerifyAutowiredFast) {
  {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    AutoRequired<dummy<1>> foo;
    AutowiredFast<dummy<1>> bar;

    ASSERT_TRUE(foo) << "AutoRequred member not created";
    EXPECT_TRUE(bar) << "AutowiredFast not satisfied";
  }

  {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    AutowiredFast<dummy<1>> fast;
    Autowired<dummy<1>> wired;

    AutoRequired<dummy<1>> required;

    ASSERT_TRUE(required) << "AutoRequired member not created";

    EXPECT_TRUE(wired.IsAutowired()) << "Deferred Autowiring wasn't satisfied";
    EXPECT_FALSE(fast) << "AutowiredFast member was deferred";
  }
}

TEST_F(AutowiringBenchmarkTest, VerifyAutowiredFastPerformance) {

  boost::chrono::nanoseconds baseline(0);
  boost::chrono::nanoseconds benchmark(0);
  {
    for (int i=0; i<500; ++i){
      AutoCreateContext ctxt;
      CurrentContextPusher pshr(ctxt);

      auto startBase = boost::chrono::high_resolution_clock::now();
      Autowired<dummy<1>>();
      Autowired<dummy<2>>();
      Autowired<dummy<3>>();
      Autowired<dummy<4>>();
      Autowired<dummy<5>>();
      Autowired<dummy<6>>();
      Autowired<dummy<7>>();
      Autowired<dummy<8>>();
      Autowired<dummy<9>>();
      Autowired<dummy<10>>();
      Autowired<dummy<11>>();
      Autowired<dummy<12>>();
      Autowired<dummy<13>>();
      Autowired<dummy<14>>();
      Autowired<dummy<15>>();
      Autowired<dummy<16>>();
      Autowired<dummy<17>>();
      Autowired<dummy<18>>();
      Autowired<dummy<19>>();
      Autowired<dummy<20>>();
      baseline += boost::chrono::high_resolution_clock::now() - startBase;

      auto startBench = boost::chrono::high_resolution_clock::now();
      AutowiredFast<dummy<21>>();
      AutowiredFast<dummy<22>>();
      AutowiredFast<dummy<23>>();
      AutowiredFast<dummy<24>>();
      AutowiredFast<dummy<25>>();
      AutowiredFast<dummy<26>>();
      AutowiredFast<dummy<27>>();
      AutowiredFast<dummy<28>>();
      AutowiredFast<dummy<29>>();
      AutowiredFast<dummy<30>>();
      AutowiredFast<dummy<31>>();
      AutowiredFast<dummy<32>>();
      AutowiredFast<dummy<33>>();
      AutowiredFast<dummy<34>>();
      AutowiredFast<dummy<35>>();
      AutowiredFast<dummy<36>>();
      AutowiredFast<dummy<37>>();
      AutowiredFast<dummy<38>>();
      AutowiredFast<dummy<39>>();
      AutowiredFast<dummy<40>>();
      benchmark += boost::chrono::high_resolution_clock::now() - startBench;
    }
  }

  EXPECT_GT(baseline, benchmark*1.75) << "Fast autowiring is slower than ordinary autowiring";
}
<commit_msg>Focusing AutowiringBenchmarkTest on already-memoized entries<commit_after>#include "stdafx.h"
#include "AutowiringBenchmarkTest.h"
#include "TestFixtures/SimpleObject.h"
#include <boost/chrono/system_clocks.hpp>

TEST_F(AutowiringBenchmarkTest, VerifySimplePerformance) {
  const size_t n = 10000;

  // Insert the object:
  AutoRequired<SimpleObject>();

  // Time n hash map hits, in order to get a baseline:
  std::unordered_map<int, int> ref;
  ref[0] = 212;
  ref[1] = 21;

  boost::chrono::nanoseconds baseline;
  {
    auto start = boost::chrono::high_resolution_clock::now();
    for(size_t i = n; i--;)
      ref[i % 2];
    baseline = boost::chrono::high_resolution_clock::now() - start;
  }

  // Time n autowirings:
  boost::chrono::nanoseconds benchmark;
  {
    auto start = boost::chrono::high_resolution_clock::now();
    for(size_t i = n; i--;)
      Autowired<SimpleObject>();
    benchmark = (boost::chrono::high_resolution_clock::now() - start) / n;
  }

  EXPECT_GT(baseline * 3. / 2., benchmark) << "Average time to autowire one member was more than 3/2 of a hash map lookup";
}

template<int N>
struct dummy:
  public virtual ContextMember
{};

void InjectDummy(void) {
  Autowired<dummy<1>>();
  Autowired<dummy<2>>();
  Autowired<dummy<3>>();
  Autowired<dummy<4>>();
  Autowired<dummy<5>>();
  Autowired<dummy<6>>();
  Autowired<dummy<7>>();
  Autowired<dummy<8>>();
  Autowired<dummy<9>>();
  Autowired<dummy<10>>();
  Autowired<dummy<11>>();
  Autowired<dummy<12>>();
  Autowired<dummy<13>>();
  Autowired<dummy<14>>();
  Autowired<dummy<15>>();
  Autowired<dummy<16>>();
  Autowired<dummy<17>>();
  Autowired<dummy<18>>();
  Autowired<dummy<19>>();
  Autowired<dummy<20>>();
  Autowired<dummy<21>>();
  Autowired<dummy<22>>();
  Autowired<dummy<23>>();
  Autowired<dummy<24>>();
  Autowired<dummy<25>>();
};

TEST_F(AutowiringBenchmarkTest, VerifyAutowiringCache) {
  boost::chrono::nanoseconds baseline(0);
  boost::chrono::nanoseconds benchmark(0);

  for(int i = 0; i<100; ++i) {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    auto startBase = boost::chrono::high_resolution_clock::now();
    InjectDummy();
    baseline += boost::chrono::high_resolution_clock::now() - startBase;

    auto startBench = boost::chrono::high_resolution_clock::now();
    InjectDummy();
    benchmark += boost::chrono::high_resolution_clock::now() - startBench;
  }

  EXPECT_GT(baseline, benchmark) << "Autowiring cache not improving performance on subsequent autowirings";
}

TEST_F(AutowiringBenchmarkTest, VerifyAutowiredFast) {
  {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    AutoRequired<dummy<1>> foo;
    AutowiredFast<dummy<1>> bar;

    ASSERT_TRUE(foo) << "AutoRequred member not created";
    EXPECT_TRUE(bar) << "AutowiredFast not satisfied";
  }

  {
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    AutowiredFast<dummy<1>> fast;
    Autowired<dummy<1>> wired;

    AutoRequired<dummy<1>> required;

    ASSERT_TRUE(required) << "AutoRequired member not created";

    EXPECT_TRUE(wired.IsAutowired()) << "Deferred Autowiring wasn't satisfied";
    EXPECT_FALSE(fast) << "AutowiredFast member was deferred";
  }
}

TEST_F(AutowiringBenchmarkTest, VerifyAutowiredFastPerformance) {
  boost::chrono::nanoseconds baseline(0);
  boost::chrono::nanoseconds benchmark(0);
  {
    // All of these tests will operate in the same context:
    AutoCreateContext ctxt;
    CurrentContextPusher pshr(ctxt);

    // Prime all memos:
    InjectDummy();

    // Test simple autowiring first:
    auto startBase = boost::chrono::high_resolution_clock::now();
    for(int i = 0; i < 500; ++i)
      InjectDummy();
    baseline += boost::chrono::high_resolution_clock::now() - startBase;

    auto startBench = boost::chrono::high_resolution_clock::now();
    for(int i = 0; i < 500; ++i) {
      AutowiredFast<dummy<1>>();
      AutowiredFast<dummy<2>>();
      AutowiredFast<dummy<3>>();
      AutowiredFast<dummy<4>>();
      AutowiredFast<dummy<5>>();
      AutowiredFast<dummy<6>>();
      AutowiredFast<dummy<7>>();
      AutowiredFast<dummy<8>>();
      AutowiredFast<dummy<9>>();
      AutowiredFast<dummy<10>>();
      AutowiredFast<dummy<11>>();
      AutowiredFast<dummy<12>>();
      AutowiredFast<dummy<13>>();
      AutowiredFast<dummy<14>>();
      AutowiredFast<dummy<15>>();
      AutowiredFast<dummy<16>>();
      AutowiredFast<dummy<17>>();
      AutowiredFast<dummy<18>>();
      AutowiredFast<dummy<19>>();
      AutowiredFast<dummy<20>>();
    }
    benchmark += boost::chrono::high_resolution_clock::now() - startBench;
  }

  EXPECT_GT(baseline, benchmark*1.75) << "Fast autowiring is slower than ordinary autowiring";
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/Benchmark.h>
#include <folly/Random.h>
#include <folly/init/Init.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include <thrift/test/gen-cpp2/DeserializationBench_types.h>

folly::Random::DefaultGenerator rng_(12345);
const int32_t kNumOfInserts = 250;

std::vector<int32_t> getRandomVector() {
  std::vector<int32_t> v;
  for (int i = 0; i < kNumOfInserts; ++i) {
    v.push_back(i);
  }
  return v;
}

std::set<int32_t> getRandomSet() {
  std::set<int32_t> s;
  for (int i = 0; i < kNumOfInserts; ++i) {
    s.insert(i);
  }
  return s;
}

std::map<int32_t, std::string> getRandomMap() {
  std::map<int32_t, std::string> m;
  for (int i = 0; i < kNumOfInserts; ++i) {
    m.emplace(i, std::to_string(i));
  }
  return m;
}

folly::sorted_vector_set<int32_t> getRandomFollySet() {
  folly::sorted_vector_set<int32_t> s;
  for (int i = 0; i < kNumOfInserts; ++i) {
    s.insert(i);
  }
  return s;
}

folly::sorted_vector_map<int32_t, std::string> getRandomFollyMap() {
  folly::sorted_vector_map<int32_t, std::string> m;
  for (int i = 0; i < kNumOfInserts; ++i) {
    m[std::move(i)] = std::to_string(i);
  }
  return m;
}

void buildRandomStructA(cpp2::StructA& obj) {
  obj.fieldA = folly::Random::rand32(rng_) % 2;
  obj.fieldB = folly::Random::rand32(rng_);
  obj.fieldC = std::to_string(folly::Random::rand32(rng_));
  obj.fieldD = getRandomVector();
  obj.fieldE = getRandomSet();
  obj.fieldF = getRandomMap();

  for (int32_t i = 0; i < kNumOfInserts; ++i) {
    std::vector<std::vector<int32_t>> g1;
    std::set<std::set<int32_t>> h1;
    std::vector<std::set<int32_t>> j1;
    std::set<std::vector<int32_t>> j2;
    for (int32_t j = 0; j < kNumOfInserts; ++j) {
      g1.push_back(getRandomVector());
      h1.insert(getRandomSet());
      j1.push_back(getRandomSet());
      j2.insert(getRandomVector());
    }
    obj.fieldG.push_back(g1);
    obj.fieldH.insert(h1);
    obj.fieldI.emplace(getRandomMap(), getRandomMap());
    obj.fieldJ.emplace(j1, j2);
  }
}

void buildRandomStructB(cpp2::StructB& obj) {
  obj.fieldA = folly::Random::rand32(rng_) % 2;
  obj.fieldB = folly::Random::rand32(rng_);
  obj.fieldC = std::to_string(folly::Random::rand32(rng_));
  obj.fieldD = getRandomVector();
  obj.fieldE = getRandomFollySet();
  obj.fieldF = getRandomFollyMap();

  for (int32_t i = 0; i < kNumOfInserts; ++i) {
    std::vector<std::vector<int32_t>> g1;
    folly::sorted_vector_set<folly::sorted_vector_set<int32_t>> h1;
    std::vector<folly::sorted_vector_set<int32_t>> j1;
    folly::sorted_vector_set<std::vector<int32_t>> j2;
    for (int32_t j = 0; j < kNumOfInserts; ++j) {
      g1.push_back(getRandomVector());
      h1.insert(getRandomFollySet());
      j1.push_back(getRandomFollySet());
      j2.insert(getRandomVector());
    }
    obj.fieldG.push_back(g1);
    obj.fieldH.insert(h1);
    obj.fieldI[std::move(getRandomFollyMap())] = getRandomFollyMap();
    obj.fieldJ[std::move(j1)] = j2;
  }
}

BENCHMARK(CompactSerialization_custom_container, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructB obj;
    buildRandomStructB(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(CompactDeserialization_custom_container, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructB obj;
    buildRandomStructB(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructB obj2;
    braces.dismissing([&] { serializer::deserialize(buf.get(), obj2); });
  }
}

BENCHMARK(CompactSerialization, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(CompactDeserialization, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructA obj2;
    braces.dismissing([&] {
      serializer::deserialize(buf.get(), obj2);
    });
  }
}

BENCHMARK(JsonSerialization, iters) {
  using serializer = apache::thrift::SimpleJSONSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(JsonDeserialization, iters) {
  using serializer = apache::thrift::SimpleJSONSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructA obj2;
    braces.dismissing([&] { serializer::deserialize(buf.get(), obj2); });
  }
}

int main(int argc, char** argv) {
  folly::init(&argc, &argv);
  folly::runBenchmarks();
  return 0;
}

/*
============================================================================
thrift/test/DeserializationBench.cpp            relative  time/iter  iters/s
============================================================================
CompactSerialization_custom_container                         2.76s  362.93m
CompactDeserialization_custom_container                    728.59ms     1.37
CompactSerialization                                          2.71s  368.92m
CompactDeserialization                                     760.46ms     1.31
JsonSerialization                                             6.02s  166.06m
JsonDeserialization                                          11.17s   89.49m
============================================================================
*/
<commit_msg>Deserialization Test: Fix incorrect move<commit_after>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/Benchmark.h>
#include <folly/Random.h>
#include <folly/init/Init.h>
#include <thrift/lib/cpp2/protocol/Serializer.h>
#include <thrift/test/gen-cpp2/DeserializationBench_types.h>

folly::Random::DefaultGenerator rng_(12345);
const int32_t kNumOfInserts = 250;

std::vector<int32_t> getRandomVector() {
  std::vector<int32_t> v;
  for (int i = 0; i < kNumOfInserts; ++i) {
    v.push_back(i);
  }
  return v;
}

std::set<int32_t> getRandomSet() {
  std::set<int32_t> s;
  for (int i = 0; i < kNumOfInserts; ++i) {
    s.insert(i);
  }
  return s;
}

std::map<int32_t, std::string> getRandomMap() {
  std::map<int32_t, std::string> m;
  for (int i = 0; i < kNumOfInserts; ++i) {
    m.emplace(i, std::to_string(i));
  }
  return m;
}

folly::sorted_vector_set<int32_t> getRandomFollySet() {
  folly::sorted_vector_set<int32_t> s;
  for (int i = 0; i < kNumOfInserts; ++i) {
    s.insert(i);
  }
  return s;
}

folly::sorted_vector_map<int32_t, std::string> getRandomFollyMap() {
  folly::sorted_vector_map<int32_t, std::string> m;
  for (int i = 0; i < kNumOfInserts; ++i) {
    m[i] = std::to_string(i);
  }
  return m;
}

void buildRandomStructA(cpp2::StructA& obj) {
  obj.fieldA = folly::Random::rand32(rng_) % 2;
  obj.fieldB = folly::Random::rand32(rng_);
  obj.fieldC = std::to_string(folly::Random::rand32(rng_));
  obj.fieldD = getRandomVector();
  obj.fieldE = getRandomSet();
  obj.fieldF = getRandomMap();

  for (int32_t i = 0; i < kNumOfInserts; ++i) {
    std::vector<std::vector<int32_t>> g1;
    std::set<std::set<int32_t>> h1;
    std::vector<std::set<int32_t>> j1;
    std::set<std::vector<int32_t>> j2;
    for (int32_t j = 0; j < kNumOfInserts; ++j) {
      g1.push_back(getRandomVector());
      h1.insert(getRandomSet());
      j1.push_back(getRandomSet());
      j2.insert(getRandomVector());
    }
    obj.fieldG.push_back(g1);
    obj.fieldH.insert(h1);
    obj.fieldI.emplace(getRandomMap(), getRandomMap());
    obj.fieldJ.emplace(j1, j2);
  }
}

void buildRandomStructB(cpp2::StructB& obj) {
  obj.fieldA = folly::Random::rand32(rng_) % 2;
  obj.fieldB = folly::Random::rand32(rng_);
  obj.fieldC = std::to_string(folly::Random::rand32(rng_));
  obj.fieldD = getRandomVector();
  obj.fieldE = getRandomFollySet();
  obj.fieldF = getRandomFollyMap();

  for (int32_t i = 0; i < kNumOfInserts; ++i) {
    std::vector<std::vector<int32_t>> g1;
    folly::sorted_vector_set<folly::sorted_vector_set<int32_t>> h1;
    std::vector<folly::sorted_vector_set<int32_t>> j1;
    folly::sorted_vector_set<std::vector<int32_t>> j2;
    for (int32_t j = 0; j < kNumOfInserts; ++j) {
      g1.push_back(getRandomVector());
      h1.insert(getRandomFollySet());
      j1.push_back(getRandomFollySet());
      j2.insert(getRandomVector());
    }
    obj.fieldG.push_back(g1);
    obj.fieldH.insert(h1);
    obj.fieldI[std::move(getRandomFollyMap())] = getRandomFollyMap();
    obj.fieldJ[std::move(j1)] = j2;
  }
}

BENCHMARK(CompactSerialization_custom_container, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructB obj;
    buildRandomStructB(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(CompactDeserialization_custom_container, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructB obj;
    buildRandomStructB(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructB obj2;
    braces.dismissing([&] { serializer::deserialize(buf.get(), obj2); });
  }
}

BENCHMARK(CompactSerialization, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(CompactDeserialization, iters) {
  using serializer = apache::thrift::CompactSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructA obj2;
    braces.dismissing([&] {
      serializer::deserialize(buf.get(), obj2);
    });
  }
}

BENCHMARK(JsonSerialization, iters) {
  using serializer = apache::thrift::SimpleJSONSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, timed:
    braces.dismissing([&] { serializer::serialize<folly::IOBufQueue>(obj); });
  }
}

BENCHMARK(JsonDeserialization, iters) {
  using serializer = apache::thrift::SimpleJSONSerializer;

  folly::BenchmarkSuspender braces; // stop the clock by default

  for (size_t i = 0; i < iters; ++i) {
    // Prep, untimed:
    cpp2::StructA obj;
    buildRandomStructA(obj);

    // Serialize, untimed:
    auto buf = serializer::serialize<folly::IOBufQueue>(obj).move();
    buf->coalesce(); // so we can ignore serialization artifacts later

    // Deserialize, timed:
    cpp2::StructA obj2;
    braces.dismissing([&] { serializer::deserialize(buf.get(), obj2); });
  }
}

int main(int argc, char** argv) {
  folly::init(&argc, &argv);
  folly::runBenchmarks();
  return 0;
}

/*
============================================================================
thrift/test/DeserializationBench.cpp            relative  time/iter  iters/s
============================================================================
CompactSerialization_custom_container                         2.76s  362.93m
CompactDeserialization_custom_container                    728.59ms     1.37
CompactSerialization                                          2.71s  368.92m
CompactDeserialization                                     760.46ms     1.31
JsonSerialization                                             6.02s  166.06m
JsonDeserialization                                          11.17s   89.49m
============================================================================
*/
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015-2016 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <fstream>
#include <memory>
#include <sstream>
#include <thread>

#include <gflags/gflags.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include "src/core/lib/gpr/string.h"
#include "src/core/lib/transport/byte_stream.h"
#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
#include "test/cpp/interop/server_helper.h"
#include "test/cpp/util/test_config.h"

DEFINE_bool(use_alts, false,
            "Whether to use alts. Enable alts will disable tls.");
DEFINE_bool(use_tls, false, "Whether to use tls.");
DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
DEFINE_int32(port, 0, "Server port.");
DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCredentials;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::SslServerCredentialsOptions;
using grpc::Status;
using grpc::WriteOptions;
using grpc::testing::InteropServerContextInspector;
using grpc::testing::Payload;
using grpc::testing::SimpleRequest;
using grpc::testing::SimpleResponse;
using grpc::testing::StreamingInputCallRequest;
using grpc::testing::StreamingInputCallResponse;
using grpc::testing::StreamingOutputCallRequest;
using grpc::testing::StreamingOutputCallResponse;
using grpc::testing::TestService;

const char kEchoInitialMetadataKey[] = "x-grpc-test-echo-initial";
const char kEchoTrailingBinMetadataKey[] = "x-grpc-test-echo-trailing-bin";
const char kEchoUserAgentKey[] = "x-grpc-test-echo-useragent";

void MaybeEchoMetadata(ServerContext* context) {
  const auto& client_metadata = context->client_metadata();
  GPR_ASSERT(client_metadata.count(kEchoInitialMetadataKey) <= 1);
  GPR_ASSERT(client_metadata.count(kEchoTrailingBinMetadataKey) <= 1);

  auto iter = client_metadata.find(kEchoInitialMetadataKey);
  if (iter != client_metadata.end()) {
    context->AddInitialMetadata(
        kEchoInitialMetadataKey,
        grpc::string(iter->second.begin(), iter->second.end()));
  }
  iter = client_metadata.find(kEchoTrailingBinMetadataKey);
  if (iter != client_metadata.end()) {
    context->AddTrailingMetadata(
        kEchoTrailingBinMetadataKey,
        grpc::string(iter->second.begin(), iter->second.end()));
  }
  // Check if client sent a magic key in the header that makes us echo
  // back the user-agent (for testing purpose)
  iter = client_metadata.find(kEchoUserAgentKey);
  if (iter != client_metadata.end()) {
    iter = client_metadata.find("user-agent");
    if (iter != client_metadata.end()) {
      context->AddInitialMetadata(
          kEchoUserAgentKey,
          grpc::string(iter->second.begin(), iter->second.end()));
    }
  }
}

bool SetPayload(int size, Payload* payload) {
  std::unique_ptr<char[]> body(new char[size]());
  payload->set_body(body.get(), size);
  return true;
}

bool CheckExpectedCompression(const ServerContext& context,
                              const bool compression_expected) {
  const InteropServerContextInspector inspector(context);
  const grpc_compression_algorithm received_compression =
      inspector.GetCallCompressionAlgorithm();

  if (compression_expected) {
    if (received_compression == GRPC_COMPRESS_NONE) {
      // Expected some compression, got NONE. This is an error.
      gpr_log(GPR_ERROR,
              "Expected compression but got uncompressed request from client.");
      return false;
    }
    if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)) {
      gpr_log(GPR_ERROR,
              "Failure: Requested compression in a compressable request, but "
              "compression bit in message flags not set.");
      return false;
    }
  } else {
    // Didn't expect compression -> make sure the request is uncompressed
    if (inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS) {
      gpr_log(GPR_ERROR,
              "Failure: Didn't requested compression, but compression bit in "
              "message flags set.");
      return false;
    }
  }
  return true;
}

class TestServiceImpl : public TestService::Service {
 public:
  Status EmptyCall(ServerContext* context, const grpc::testing::Empty* request,
                   grpc::testing::Empty* response) {
    MaybeEchoMetadata(context);
    return Status::OK;
  }

  // Response contains current timestamp. We ignore everything in the request.
  Status CacheableUnaryCall(ServerContext* context,
                            const SimpleRequest* request,
                            SimpleResponse* response) {
    gpr_timespec ts = gpr_now(GPR_CLOCK_PRECISE);
    std::string timestamp = std::to_string((long long unsigned)ts.tv_nsec);
    response->mutable_payload()->set_body(timestamp.c_str(), timestamp.size());
    context->AddInitialMetadata("cache-control", "max-age=60, public");
    return Status::OK;
  }

  Status UnaryCall(ServerContext* context, const SimpleRequest* request,
                   SimpleResponse* response) {
    MaybeEchoMetadata(context);
    if (request->has_response_compressed()) {
      const bool compression_requested = request->response_compressed().value();
      gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s",
              compression_requested ? "enabled" : "disabled", __func__);
      if (compression_requested) {
        // Any level would do, let's go for HIGH because we are overachievers.
        context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH);
      } else {
        context->set_compression_level(GRPC_COMPRESS_LEVEL_NONE);
      }
    }
    if (!CheckExpectedCompression(*context,
                                  request->expect_compressed().value())) {
      return Status(grpc::StatusCode::INVALID_ARGUMENT,
                    "Compressed request expectation not met.");
    }
    if (request->response_size() > 0) {
      if (!SetPayload(request->response_size(), response->mutable_payload())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Error creating payload.");
      }
    }

    if (request->has_response_status()) {
      return Status(
          static_cast<grpc::StatusCode>(request->response_status().code()),
          request->response_status().message());
    }

    return Status::OK;
  }

  Status StreamingOutputCall(
      ServerContext* context, const StreamingOutputCallRequest* request,
      ServerWriter<StreamingOutputCallResponse>* writer) {
    StreamingOutputCallResponse response;
    bool write_success = true;
    for (int i = 0; write_success && i < request->response_parameters_size();
         i++) {
      if (!SetPayload(request->response_parameters(i).size(),
                      response.mutable_payload())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Error creating payload.");
      }
      WriteOptions wopts;
      if (request->response_parameters(i).has_compressed()) {
        // Compress by default. Disabled on a per-message basis.
        context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH);
        const bool compression_requested =
            request->response_parameters(i).compressed().value();
        gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s",
                compression_requested ? "enabled" : "disabled", __func__);
        if (!compression_requested) {
          wopts.set_no_compression();
        }  // else, compression is already enabled via the context.
      }
      int time_us;
      if ((time_us = request->response_parameters(i).interval_us()) > 0) {
        // Sleep before response if needed
        gpr_timespec sleep_time =
            gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                         gpr_time_from_micros(time_us, GPR_TIMESPAN));
        gpr_sleep_until(sleep_time);
      }
      write_success = writer->Write(response, wopts);
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }

  Status StreamingInputCall(ServerContext* context,
                            ServerReader<StreamingInputCallRequest>* reader,
                            StreamingInputCallResponse* response) {
    StreamingInputCallRequest request;
    int aggregated_payload_size = 0;
    while (reader->Read(&request)) {
      if (!CheckExpectedCompression(*context,
                                    request.expect_compressed().value())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Compressed request expectation not met.");
      }
      if (request.has_payload()) {
        aggregated_payload_size += request.payload().body().size();
      }
    }
    response->set_aggregated_payload_size(aggregated_payload_size);
    return Status::OK;
  }

  Status FullDuplexCall(
      ServerContext* context,
      ServerReaderWriter<StreamingOutputCallResponse,
                         StreamingOutputCallRequest>* stream) {
    MaybeEchoMetadata(context);
    StreamingOutputCallRequest request;
    StreamingOutputCallResponse response;
    bool write_success = true;
    while (write_success && stream->Read(&request)) {
      if (request.has_response_status()) {
        return Status(
            static_cast<grpc::StatusCode>(request.response_status().code()),
            request.response_status().message());
      }
      if (request.response_parameters_size() != 0) {
        response.mutable_payload()->set_type(request.payload().type());
        response.mutable_payload()->set_body(
            grpc::string(request.response_parameters(0).size(), '\0'));
        int time_us;
        if ((time_us = request.response_parameters(0).interval_us()) > 0) {
          // Sleep before response if needed
          gpr_timespec sleep_time =
              gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                           gpr_time_from_micros(time_us, GPR_TIMESPAN));
          gpr_sleep_until(sleep_time);
        }
        write_success = stream->Write(response);
      }
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }

  Status HalfDuplexCall(
      ServerContext* context,
      ServerReaderWriter<StreamingOutputCallResponse,
                         StreamingOutputCallRequest>* stream) {
    std::vector<StreamingOutputCallRequest> requests;
    StreamingOutputCallRequest request;
    while (stream->Read(&request)) {
      requests.push_back(request);
    }

    StreamingOutputCallResponse response;
    bool write_success = true;
    for (unsigned int i = 0; write_success && i < requests.size(); i++) {
      response.mutable_payload()->set_type(requests[i].payload().type());
      if (requests[i].response_parameters_size() == 0) {
        return Status(grpc::StatusCode::INTERNAL,
                      "Request does not have response parameters.");
      }
      response.mutable_payload()->set_body(
          grpc::string(requests[i].response_parameters(0).size(), '\0'));
      write_success = stream->Write(response);
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }
};

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds) {
  RunServer(creds, FLAGS_port, nullptr, nullptr);
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds,
    std::unique_ptr<std::vector<std::unique_ptr<ServerBuilderOption>>>
        server_options) {
  RunServer(creds, FLAGS_port, nullptr, std::move(server_options));
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds, const int port,
    ServerStartedCondition* server_started_condition) {
  RunServer(creds, FLAGS_port, server_started_condition, nullptr);
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds, const int port,
    ServerStartedCondition* server_started_condition,
    std::unique_ptr<std::vector<std::unique_ptr<ServerBuilderOption>>>
        server_options) {
  GPR_ASSERT(port != 0);
  std::ostringstream server_address;
  server_address << "0.0.0.0:" << port;
  TestServiceImpl service;

  SimpleRequest request;
  SimpleResponse response;

  ServerBuilder builder;
  builder.RegisterService(&service);
  builder.AddListeningPort(server_address.str(), creds);
  if (server_options != nullptr) {
    for (size_t i = 0; i < server_options->size(); i++) {
      builder.SetOption(std::move((*server_options)[i]));
    }
  }
  if (FLAGS_max_send_message_size >= 0) {
    builder.SetMaxSendMessageSize(FLAGS_max_send_message_size);
  }
  std::unique_ptr<Server> server(builder.BuildAndStart());
  gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());

  // Signal that the server has started.
  if (server_started_condition) {
    std::unique_lock<std::mutex> lock(server_started_condition->mutex);
    server_started_condition->server_started = true;
    server_started_condition->condition.notify_all();
  }

  while (!gpr_atm_no_barrier_load(&g_got_sigint)) {
    gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                                 gpr_time_from_seconds(5, GPR_TIMESPAN)));
  }
}
<commit_msg>Fix bug in RunServer()<commit_after>/*
 *
 * Copyright 2015-2016 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <fstream>
#include <memory>
#include <sstream>
#include <thread>

#include <gflags/gflags.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <grpcpp/server_context.h>

#include "src/core/lib/gpr/string.h"
#include "src/core/lib/transport/byte_stream.h"
#include "src/proto/grpc/testing/empty.pb.h"
#include "src/proto/grpc/testing/messages.pb.h"
#include "src/proto/grpc/testing/test.grpc.pb.h"
#include "test/cpp/interop/server_helper.h"
#include "test/cpp/util/test_config.h"

DEFINE_bool(use_alts, false,
            "Whether to use alts. Enable alts will disable tls.");
DEFINE_bool(use_tls, false, "Whether to use tls.");
DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
DEFINE_int32(port, 0, "Server port.");
DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCredentials;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using grpc::ServerWriter;
using grpc::SslServerCredentialsOptions;
using grpc::Status;
using grpc::WriteOptions;
using grpc::testing::InteropServerContextInspector;
using grpc::testing::Payload;
using grpc::testing::SimpleRequest;
using grpc::testing::SimpleResponse;
using grpc::testing::StreamingInputCallRequest;
using grpc::testing::StreamingInputCallResponse;
using grpc::testing::StreamingOutputCallRequest;
using grpc::testing::StreamingOutputCallResponse;
using grpc::testing::TestService;

const char kEchoInitialMetadataKey[] = "x-grpc-test-echo-initial";
const char kEchoTrailingBinMetadataKey[] = "x-grpc-test-echo-trailing-bin";
const char kEchoUserAgentKey[] = "x-grpc-test-echo-useragent";

void MaybeEchoMetadata(ServerContext* context) {
  const auto& client_metadata = context->client_metadata();
  GPR_ASSERT(client_metadata.count(kEchoInitialMetadataKey) <= 1);
  GPR_ASSERT(client_metadata.count(kEchoTrailingBinMetadataKey) <= 1);

  auto iter = client_metadata.find(kEchoInitialMetadataKey);
  if (iter != client_metadata.end()) {
    context->AddInitialMetadata(
        kEchoInitialMetadataKey,
        grpc::string(iter->second.begin(), iter->second.end()));
  }
  iter = client_metadata.find(kEchoTrailingBinMetadataKey);
  if (iter != client_metadata.end()) {
    context->AddTrailingMetadata(
        kEchoTrailingBinMetadataKey,
        grpc::string(iter->second.begin(), iter->second.end()));
  }
  // Check if client sent a magic key in the header that makes us echo
  // back the user-agent (for testing purpose)
  iter = client_metadata.find(kEchoUserAgentKey);
  if (iter != client_metadata.end()) {
    iter = client_metadata.find("user-agent");
    if (iter != client_metadata.end()) {
      context->AddInitialMetadata(
          kEchoUserAgentKey,
          grpc::string(iter->second.begin(), iter->second.end()));
    }
  }
}

bool SetPayload(int size, Payload* payload) {
  std::unique_ptr<char[]> body(new char[size]());
  payload->set_body(body.get(), size);
  return true;
}

bool CheckExpectedCompression(const ServerContext& context,
                              const bool compression_expected) {
  const InteropServerContextInspector inspector(context);
  const grpc_compression_algorithm received_compression =
      inspector.GetCallCompressionAlgorithm();

  if (compression_expected) {
    if (received_compression == GRPC_COMPRESS_NONE) {
      // Expected some compression, got NONE. This is an error.
      gpr_log(GPR_ERROR,
              "Expected compression but got uncompressed request from client.");
      return false;
    }
    if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)) {
      gpr_log(GPR_ERROR,
              "Failure: Requested compression in a compressable request, but "
              "compression bit in message flags not set.");
      return false;
    }
  } else {
    // Didn't expect compression -> make sure the request is uncompressed
    if (inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS) {
      gpr_log(GPR_ERROR,
              "Failure: Didn't requested compression, but compression bit in "
              "message flags set.");
      return false;
    }
  }
  return true;
}

class TestServiceImpl : public TestService::Service {
 public:
  Status EmptyCall(ServerContext* context, const grpc::testing::Empty* request,
                   grpc::testing::Empty* response) {
    MaybeEchoMetadata(context);
    return Status::OK;
  }

  // Response contains current timestamp. We ignore everything in the request.
  Status CacheableUnaryCall(ServerContext* context,
                            const SimpleRequest* request,
                            SimpleResponse* response) {
    gpr_timespec ts = gpr_now(GPR_CLOCK_PRECISE);
    std::string timestamp = std::to_string((long long unsigned)ts.tv_nsec);
    response->mutable_payload()->set_body(timestamp.c_str(), timestamp.size());
    context->AddInitialMetadata("cache-control", "max-age=60, public");
    return Status::OK;
  }

  Status UnaryCall(ServerContext* context, const SimpleRequest* request,
                   SimpleResponse* response) {
    MaybeEchoMetadata(context);
    if (request->has_response_compressed()) {
      const bool compression_requested = request->response_compressed().value();
      gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s",
              compression_requested ? "enabled" : "disabled", __func__);
      if (compression_requested) {
        // Any level would do, let's go for HIGH because we are overachievers.
        context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH);
      } else {
        context->set_compression_level(GRPC_COMPRESS_LEVEL_NONE);
      }
    }
    if (!CheckExpectedCompression(*context,
                                  request->expect_compressed().value())) {
      return Status(grpc::StatusCode::INVALID_ARGUMENT,
                    "Compressed request expectation not met.");
    }
    if (request->response_size() > 0) {
      if (!SetPayload(request->response_size(), response->mutable_payload())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Error creating payload.");
      }
    }

    if (request->has_response_status()) {
      return Status(
          static_cast<grpc::StatusCode>(request->response_status().code()),
          request->response_status().message());
    }

    return Status::OK;
  }

  Status StreamingOutputCall(
      ServerContext* context, const StreamingOutputCallRequest* request,
      ServerWriter<StreamingOutputCallResponse>* writer) {
    StreamingOutputCallResponse response;
    bool write_success = true;
    for (int i = 0; write_success && i < request->response_parameters_size();
         i++) {
      if (!SetPayload(request->response_parameters(i).size(),
                      response.mutable_payload())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Error creating payload.");
      }
      WriteOptions wopts;
      if (request->response_parameters(i).has_compressed()) {
        // Compress by default. Disabled on a per-message basis.
        context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH);
        const bool compression_requested =
            request->response_parameters(i).compressed().value();
        gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s",
                compression_requested ? "enabled" : "disabled", __func__);
        if (!compression_requested) {
          wopts.set_no_compression();
        }  // else, compression is already enabled via the context.
      }
      int time_us;
      if ((time_us = request->response_parameters(i).interval_us()) > 0) {
        // Sleep before response if needed
        gpr_timespec sleep_time =
            gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                         gpr_time_from_micros(time_us, GPR_TIMESPAN));
        gpr_sleep_until(sleep_time);
      }
      write_success = writer->Write(response, wopts);
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }

  Status StreamingInputCall(ServerContext* context,
                            ServerReader<StreamingInputCallRequest>* reader,
                            StreamingInputCallResponse* response) {
    StreamingInputCallRequest request;
    int aggregated_payload_size = 0;
    while (reader->Read(&request)) {
      if (!CheckExpectedCompression(*context,
                                    request.expect_compressed().value())) {
        return Status(grpc::StatusCode::INVALID_ARGUMENT,
                      "Compressed request expectation not met.");
      }
      if (request.has_payload()) {
        aggregated_payload_size += request.payload().body().size();
      }
    }
    response->set_aggregated_payload_size(aggregated_payload_size);
    return Status::OK;
  }

  Status FullDuplexCall(
      ServerContext* context,
      ServerReaderWriter<StreamingOutputCallResponse,
                         StreamingOutputCallRequest>* stream) {
    MaybeEchoMetadata(context);
    StreamingOutputCallRequest request;
    StreamingOutputCallResponse response;
    bool write_success = true;
    while (write_success && stream->Read(&request)) {
      if (request.has_response_status()) {
        return Status(
            static_cast<grpc::StatusCode>(request.response_status().code()),
            request.response_status().message());
      }
      if (request.response_parameters_size() != 0) {
        response.mutable_payload()->set_type(request.payload().type());
        response.mutable_payload()->set_body(
            grpc::string(request.response_parameters(0).size(), '\0'));
        int time_us;
        if ((time_us = request.response_parameters(0).interval_us()) > 0) {
          // Sleep before response if needed
          gpr_timespec sleep_time =
              gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                           gpr_time_from_micros(time_us, GPR_TIMESPAN));
          gpr_sleep_until(sleep_time);
        }
        write_success = stream->Write(response);
      }
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }

  Status HalfDuplexCall(
      ServerContext* context,
      ServerReaderWriter<StreamingOutputCallResponse,
                         StreamingOutputCallRequest>* stream) {
    std::vector<StreamingOutputCallRequest> requests;
    StreamingOutputCallRequest request;
    while (stream->Read(&request)) {
      requests.push_back(request);
    }

    StreamingOutputCallResponse response;
    bool write_success = true;
    for (unsigned int i = 0; write_success && i < requests.size(); i++) {
      response.mutable_payload()->set_type(requests[i].payload().type());
      if (requests[i].response_parameters_size() == 0) {
        return Status(grpc::StatusCode::INTERNAL,
                      "Request does not have response parameters.");
      }
      response.mutable_payload()->set_body(
          grpc::string(requests[i].response_parameters(0).size(), '\0'));
      write_success = stream->Write(response);
    }
    if (write_success) {
      return Status::OK;
    } else {
      return Status(grpc::StatusCode::INTERNAL, "Error writing response.");
    }
  }
};

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds) {
  RunServer(creds, FLAGS_port, nullptr, nullptr);
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds,
    std::unique_ptr<std::vector<std::unique_ptr<ServerBuilderOption>>>
        server_options) {
  RunServer(creds, FLAGS_port, nullptr, std::move(server_options));
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds, const int port,
    ServerStartedCondition* server_started_condition) {
  RunServer(creds, port, server_started_condition, nullptr);
}

void grpc::testing::interop::RunServer(
    std::shared_ptr<ServerCredentials> creds, const int port,
    ServerStartedCondition* server_started_condition,
    std::unique_ptr<std::vector<std::unique_ptr<ServerBuilderOption>>>
        server_options) {
  GPR_ASSERT(port != 0);
  std::ostringstream server_address;
  server_address << "0.0.0.0:" << port;
  TestServiceImpl service;

  SimpleRequest request;
  SimpleResponse response;

  ServerBuilder builder;
  builder.RegisterService(&service);
  builder.AddListeningPort(server_address.str(), creds);
  if (server_options != nullptr) {
    for (size_t i = 0; i < server_options->size(); i++) {
      builder.SetOption(std::move((*server_options)[i]));
    }
  }
  if (FLAGS_max_send_message_size >= 0) {
    builder.SetMaxSendMessageSize(FLAGS_max_send_message_size);
  }
  std::unique_ptr<Server> server(builder.BuildAndStart());
  gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());

  // Signal that the server has started.
  if (server_started_condition) {
    std::unique_lock<std::mutex> lock(server_started_condition->mutex);
    server_started_condition->server_started = true;
    server_started_condition->condition.notify_all();
  }

  while (!gpr_atm_no_barrier_load(&g_got_sigint)) {
    gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                                 gpr_time_from_seconds(5, GPR_TIMESPAN)));
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <random>
#include <thread>

#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/base/test/test_task_runner.h"
#include "src/traced/probes/ftrace/ftrace_controller.h"
#include "src/traced/probes/ftrace/ftrace_procfs.h"
#include "src/tracing/ipc/default_socket.h"
#include "test/task_runner_thread.h"
#include "test/task_runner_thread_delegates.h"
#include "test/test_helper.h"

#include "perfetto/trace/trace_packet.pb.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {

namespace {

class PerfettoTest : public ::testing::Test {
 public:
  void SetUp() override {
    // TODO(primiano): refactor this, it's copy/pasted in three places now.
    size_t index = 0;
    constexpr auto kTracingPaths = FtraceController::kTracingPaths;
    while (!ftrace_procfs_ && kTracingPaths[index]) {
      ftrace_procfs_ = FtraceProcfs::Create(kTracingPaths[index++]);
    }
    if (!ftrace_procfs_)
      return;
    ftrace_procfs_->SetTracingOn(false);
  }

  void TearDown() override {
    if (ftrace_procfs_)
      ftrace_procfs_->SetTracingOn(false);
  }

  std::unique_ptr<FtraceProcfs> ftrace_procfs_;
};

}  // namespace

// If we're building on Android and starting the daemons ourselves,
// create the sockets in a world-writable location.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
    PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
#else
#define TEST_PRODUCER_SOCK_NAME ::perfetto::GetProducerSocket()
#endif

// TODO(b/73453011): reenable on more platforms (including standalone Android).
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
#define TreeHuggerOnly(x) x
#else
#define TreeHuggerOnly(x) DISABLED_##x
#endif

TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceProducer)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(1024);
  trace_config.set_duration_ms(3000);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("linux.ftrace");
  ds_config->set_target_buffer(0);

  auto* ftrace_config = ds_config->mutable_ftrace_config();
  *ftrace_config->add_ftrace_events() = "sched_switch";
  *ftrace_config->add_ftrace_events() = "bar";

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_GT(packets.size(), 0u);

  for (const auto& packet : packets) {
    for (int ev = 0; ev < packet.ftrace_events().event_size(); ev++) {
      ASSERT_TRUE(packet.ftrace_events().event(ev).has_sched_switch());
    }
  }
}

TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  const uint32_t kTestTimeoutMs = 30000;
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(16);
  trace_config.set_duration_ms(kTestTimeoutMs);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("linux.ftrace");

  auto* ftrace_config = ds_config->mutable_ftrace_config();
  *ftrace_config->add_ftrace_events() = "print";

  helper.StartTracing(trace_config);

  // Do a first flush just to synchronize with the producer. The problem here
  // is that, on a Linux workstation, the producer can take several seconds just
  // to get to the point where ftrace is ready. We use the flush ack as a
  // synchronization point.
  helper.FlushAndWait(kTestTimeoutMs);

  EXPECT_TRUE(ftrace_procfs_->IsTracingEnabled());
  const char kMarker[] = "just_one_event";
  EXPECT_TRUE(ftrace_procfs_->WriteTraceMarker(kMarker));

  // This is the real flush we are testing.
  helper.FlushAndWait(kTestTimeoutMs);

  helper.DisableTracing();
  helper.WaitForTracingDisabled(kTestTimeoutMs);

  helper.ReadData();
  helper.WaitForReadData();

  int marker_found = 0;
  for (const auto& packet : helper.trace()) {
    for (int i = 0; i < packet.ftrace_events().event_size(); i++) {
      const auto& ev = packet.ftrace_events().event(i);
      if (ev.has_print() && ev.print().buf().find(kMarker) != std::string::npos)
        marker_found++;
    }
  }
  ASSERT_EQ(marker_found, 1);
}

TEST_F(PerfettoTest, TreeHuggerOnly(TestBatteryTracing)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#else
  base::ignore_result(TEST_PRODUCER_SOCK_NAME);
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(128);
  trace_config.set_duration_ms(3000);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.power");
  ds_config->set_target_buffer(0);
  auto* power_config = ds_config->mutable_android_power_config();
  power_config->set_battery_poll_ms(250);
  *power_config->add_battery_counters() =
      AndroidPowerConfig::BATTERY_COUNTER_CHARGE;
  *power_config->add_battery_counters() =
      AndroidPowerConfig::BATTERY_COUNTER_CAPACITY_PERCENT;

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_GT(packets.size(), 0u);

  bool has_battery_packet = false;
  for (const auto& packet : packets) {
    if (!packet.has_battery())
      continue;
    has_battery_packet = true;
    EXPECT_GE(packet.battery().charge_counter_uah(), 0);
    EXPECT_GE(packet.battery().capacity_percent(), 0);
    EXPECT_LE(packet.battery().capacity_percent(), 100);
  }

  ASSERT_TRUE(has_battery_packet);
}

TEST_F(PerfettoTest, TestFakeProducer) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();
  helper.ConnectFakeProducer();
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(1024);
  trace_config.set_duration_ms(200);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.perfetto.FakeProducer");
  ds_config->set_target_buffer(0);

  static constexpr size_t kNumPackets = 10;
  static constexpr uint32_t kRandomSeed = 42;
  static constexpr uint32_t kMsgSize = 1024;
  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
  ds_config->mutable_for_testing()->set_send_batch_on_register(true);

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_EQ(packets.size(), kNumPackets);

  std::minstd_rand0 rnd_engine(kRandomSeed);
  for (const auto& packet : packets) {
    ASSERT_TRUE(packet.has_for_testing());
    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
  }
}

TEST_F(PerfettoTest, VeryLargePackets) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();
  helper.ConnectFakeProducer();
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  trace_config.set_duration_ms(500);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.perfetto.FakeProducer");
  ds_config->set_target_buffer(0);

  static constexpr size_t kNumPackets = 5;
  static constexpr uint32_t kRandomSeed = 42;
  static constexpr uint32_t kMsgSize = 1024 * 1024 - 42;
  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
  ds_config->mutable_for_testing()->set_send_batch_on_register(true);

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_EQ(packets.size(), kNumPackets);

  std::minstd_rand0 rnd_engine(kRandomSeed);
  for (const auto& packet : packets) {
    ASSERT_TRUE(packet.has_for_testing());
    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
    size_t msg_size = packet.for_testing().str().size();
    ASSERT_EQ(kMsgSize, msg_size);
    for (size_t i = 0; i < msg_size; i++)
      ASSERT_EQ(i < msg_size - 1 ? '.' : 0, packet.for_testing().str()[i]);
  }
}

}  // namespace perfetto
<commit_msg>Remove charge counter assertions in tests am: 61804c04b3 am: 6cc6a48c42<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <random>
#include <thread>

#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/base/test/test_task_runner.h"
#include "src/traced/probes/ftrace/ftrace_controller.h"
#include "src/traced/probes/ftrace/ftrace_procfs.h"
#include "src/tracing/ipc/default_socket.h"
#include "test/task_runner_thread.h"
#include "test/task_runner_thread_delegates.h"
#include "test/test_helper.h"

#include "perfetto/trace/trace_packet.pb.h"
#include "perfetto/trace/trace_packet.pbzero.h"

namespace perfetto {

namespace {

class PerfettoTest : public ::testing::Test {
 public:
  void SetUp() override {
    // TODO(primiano): refactor this, it's copy/pasted in three places now.
    size_t index = 0;
    constexpr auto kTracingPaths = FtraceController::kTracingPaths;
    while (!ftrace_procfs_ && kTracingPaths[index]) {
      ftrace_procfs_ = FtraceProcfs::Create(kTracingPaths[index++]);
    }
    if (!ftrace_procfs_)
      return;
    ftrace_procfs_->SetTracingOn(false);
  }

  void TearDown() override {
    if (ftrace_procfs_)
      ftrace_procfs_->SetTracingOn(false);
  }

  std::unique_ptr<FtraceProcfs> ftrace_procfs_;
};

}  // namespace

// If we're building on Android and starting the daemons ourselves,
// create the sockets in a world-writable location.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
    PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
#else
#define TEST_PRODUCER_SOCK_NAME ::perfetto::GetProducerSocket()
#endif

// TODO(b/73453011): reenable on more platforms (including standalone Android).
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
#define TreeHuggerOnly(x) x
#else
#define TreeHuggerOnly(x) DISABLED_##x
#endif

TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceProducer)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(1024);
  trace_config.set_duration_ms(3000);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("linux.ftrace");
  ds_config->set_target_buffer(0);

  auto* ftrace_config = ds_config->mutable_ftrace_config();
  *ftrace_config->add_ftrace_events() = "sched_switch";
  *ftrace_config->add_ftrace_events() = "bar";

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_GT(packets.size(), 0u);

  for (const auto& packet : packets) {
    for (int ev = 0; ev < packet.ftrace_events().event_size(); ev++) {
      ASSERT_TRUE(packet.ftrace_events().event(ev).has_sched_switch());
    }
  }
}

TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  const uint32_t kTestTimeoutMs = 30000;
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(16);
  trace_config.set_duration_ms(kTestTimeoutMs);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("linux.ftrace");

  auto* ftrace_config = ds_config->mutable_ftrace_config();
  *ftrace_config->add_ftrace_events() = "print";

  helper.StartTracing(trace_config);

  // Do a first flush just to synchronize with the producer. The problem here
  // is that, on a Linux workstation, the producer can take several seconds just
  // to get to the point where ftrace is ready. We use the flush ack as a
  // synchronization point.
  helper.FlushAndWait(kTestTimeoutMs);

  EXPECT_TRUE(ftrace_procfs_->IsTracingEnabled());
  const char kMarker[] = "just_one_event";
  EXPECT_TRUE(ftrace_procfs_->WriteTraceMarker(kMarker));

  // This is the real flush we are testing.
  helper.FlushAndWait(kTestTimeoutMs);

  helper.DisableTracing();
  helper.WaitForTracingDisabled(kTestTimeoutMs);

  helper.ReadData();
  helper.WaitForReadData();

  int marker_found = 0;
  for (const auto& packet : helper.trace()) {
    for (int i = 0; i < packet.ftrace_events().event_size(); i++) {
      const auto& ev = packet.ftrace_events().event(i);
      if (ev.has_print() && ev.print().buf().find(kMarker) != std::string::npos)
        marker_found++;
    }
  }
  ASSERT_EQ(marker_found, 1);
}

TEST_F(PerfettoTest, TreeHuggerOnly(TestBatteryTracing)) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();

#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
  TaskRunnerThread producer_thread("perfetto.prd");
  producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
      new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
#else
  base::ignore_result(TEST_PRODUCER_SOCK_NAME);
#endif

  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(128);
  trace_config.set_duration_ms(3000);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.power");
  ds_config->set_target_buffer(0);
  auto* power_config = ds_config->mutable_android_power_config();
  power_config->set_battery_poll_ms(250);
  *power_config->add_battery_counters() =
      AndroidPowerConfig::BATTERY_COUNTER_CHARGE;
  *power_config->add_battery_counters() =
      AndroidPowerConfig::BATTERY_COUNTER_CAPACITY_PERCENT;

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_GT(packets.size(), 0u);

  bool has_battery_packet = false;
  for (const auto& packet : packets) {
    if (!packet.has_battery())
      continue;
    has_battery_packet = true;
    // Unfortunately we cannot make any assertions on the charge counter.
    // On some devices it can reach negative values (b/64685329).
    EXPECT_GE(packet.battery().capacity_percent(), 0);
    EXPECT_LE(packet.battery().capacity_percent(), 100);
  }

  ASSERT_TRUE(has_battery_packet);
}

TEST_F(PerfettoTest, TestFakeProducer) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();
  helper.ConnectFakeProducer();
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(1024);
  trace_config.set_duration_ms(200);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.perfetto.FakeProducer");
  ds_config->set_target_buffer(0);

  static constexpr size_t kNumPackets = 10;
  static constexpr uint32_t kRandomSeed = 42;
  static constexpr uint32_t kMsgSize = 1024;
  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
  ds_config->mutable_for_testing()->set_send_batch_on_register(true);

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_EQ(packets.size(), kNumPackets);

  std::minstd_rand0 rnd_engine(kRandomSeed);
  for (const auto& packet : packets) {
    ASSERT_TRUE(packet.has_for_testing());
    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
  }
}

TEST_F(PerfettoTest, VeryLargePackets) {
  base::TestTaskRunner task_runner;

  TestHelper helper(&task_runner);
  helper.StartServiceIfRequired();
  helper.ConnectFakeProducer();
  helper.ConnectConsumer();
  helper.WaitForConsumerConnect();

  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  trace_config.set_duration_ms(500);

  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("android.perfetto.FakeProducer");
  ds_config->set_target_buffer(0);

  static constexpr size_t kNumPackets = 5;
  static constexpr uint32_t kRandomSeed = 42;
  static constexpr uint32_t kMsgSize = 1024 * 1024 - 42;
  ds_config->mutable_for_testing()->set_seed(kRandomSeed);
  ds_config->mutable_for_testing()->set_message_count(kNumPackets);
  ds_config->mutable_for_testing()->set_message_size(kMsgSize);
  ds_config->mutable_for_testing()->set_send_batch_on_register(true);

  helper.StartTracing(trace_config);
  helper.WaitForTracingDisabled();

  helper.ReadData();
  helper.WaitForReadData();

  const auto& packets = helper.trace();
  ASSERT_EQ(packets.size(), kNumPackets);

  std::minstd_rand0 rnd_engine(kRandomSeed);
  for (const auto& packet : packets) {
    ASSERT_TRUE(packet.has_for_testing());
    ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
    size_t msg_size = packet.for_testing().str().size();
    ASSERT_EQ(kMsgSize, msg_size);
    for (size_t i = 0; i < msg_size; i++)
      ASSERT_EQ(i < msg_size - 1 ? '.' : 0, packet.for_testing().str()[i]);
  }
}

}  // namespace perfetto
<|endoftext|>