<commit_before>/*
 * Copyright (C) 2017 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 "src/tracing/core/service_impl.h"

#include <string.h>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/producer.h"
#include "perfetto/tracing/core/shared_memory.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/test/test_shared_memory.h"

namespace perfetto {
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Mock;

namespace {

class MockProducer : public Producer {
 public:
  ~MockProducer() override {}

  // Producer implementation.
  MOCK_METHOD0(OnConnect, void());
  MOCK_METHOD0(OnDisconnect, void());
  MOCK_METHOD2(CreateDataSourceInstance,
               void(DataSourceInstanceID, const DataSourceConfig&));
  MOCK_METHOD1(TearDownDataSourceInstance, void(DataSourceInstanceID));
  MOCK_METHOD0(OnTracingStart, void());
  MOCK_METHOD0(OnTracingStop, void());
};

class MockConsumer : public Consumer {
 public:
  ~MockConsumer() override {}

  // Consumer implementation.
  MOCK_METHOD0(OnConnect, void());
  MOCK_METHOD0(OnDisconnect, void());

  void OnTraceData(std::vector<TracePacket> packets, bool has_more) override {}
};

}  // namespace

class ServiceImplTest : public testing::Test {
 public:
  ServiceImplTest() {
    auto shm_factory =
        std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
    svc.reset(static_cast<ServiceImpl*>(
        Service::CreateInstance(std::move(shm_factory), &task_runner)
            .release()));
  }

  base::TestTaskRunner task_runner;
  std::unique_ptr<ServiceImpl> svc;
};

TEST_F(ServiceImplTest, RegisterAndUnregister) {
  MockProducer mock_producer_1;
  MockProducer mock_producer_2;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_1 =
      svc->ConnectProducer(&mock_producer_1, 123u /* uid */);
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_2 =
      svc->ConnectProducer(&mock_producer_2, 456u /* uid */);

  ASSERT_TRUE(producer_endpoint_1);
  ASSERT_TRUE(producer_endpoint_2);

  InSequence seq;
  EXPECT_CALL(mock_producer_1, OnConnect());
  EXPECT_CALL(mock_producer_2, OnConnect());
  task_runner.RunUntilIdle();

  ASSERT_EQ(2u, svc->num_producers());
  ASSERT_EQ(producer_endpoint_1.get(), svc->GetProducer(1));
  ASSERT_EQ(producer_endpoint_2.get(), svc->GetProducer(2));
  ASSERT_EQ(123u, svc->GetProducer(1)->uid_);
  ASSERT_EQ(456u, svc->GetProducer(2)->uid_);

  DataSourceDescriptor ds_desc1;
  ds_desc1.set_name("foo");
  producer_endpoint_1->RegisterDataSource(
      ds_desc1, [this, &producer_endpoint_1](DataSourceID id) {
        EXPECT_EQ(1u, id);
        task_runner.PostTask(
            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
                      producer_endpoint_1.get(), id));
      });

  DataSourceDescriptor ds_desc2;
  ds_desc2.set_name("bar");
  producer_endpoint_2->RegisterDataSource(
      ds_desc2, [this, &producer_endpoint_2](DataSourceID id) {
        EXPECT_EQ(1u, id);
        task_runner.PostTask(
            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
                      producer_endpoint_2.get(), id));
      });

  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer_1, OnDisconnect());
  producer_endpoint_1.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer_1);

  ASSERT_EQ(1u, svc->num_producers());
  ASSERT_EQ(nullptr, svc->GetProducer(1));

  EXPECT_CALL(mock_producer_2, OnDisconnect());
  producer_endpoint_2.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer_2);

  ASSERT_EQ(0u, svc->num_producers());
}
TEST_F(ServiceImplTest, EnableAndDisableTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});

  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint->DisableTracing();
  producer_endpoint.reset();
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, LockdownMode) {
  MockConsumer mock_consumer;
  EXPECT_CALL(mock_consumer, OnConnect());
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  TraceConfig trace_config;
  trace_config.set_lockdown_mode(
      TraceConfig::LockdownModeOperation::LOCKDOWN_SET);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  InSequence seq;

  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, geteuid() + 1 /* uid */);

  MockProducer mock_producer_sameuid;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_sameuid =
      svc->ConnectProducer(&mock_producer_sameuid, geteuid() /* uid */);

  EXPECT_CALL(mock_producer, OnConnect()).Times(0);
  EXPECT_CALL(mock_producer_sameuid, OnConnect());
  task_runner.RunUntilIdle();

  Mock::VerifyAndClearExpectations(&mock_producer);

  consumer_endpoint->DisableTracing();
  task_runner.RunUntilIdle();

  trace_config.set_lockdown_mode(
      TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnConnect());
  producer_endpoint_sameuid =
      svc->ConnectProducer(&mock_producer, geteuid() + 1);

  task_runner.RunUntilIdle();
}

TEST_F(ServiceImplTest, DisconnectConsumerWhileTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  // Disconnecting the consumer while tracing should trigger data source
  // teardown.
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  producer_endpoint.reset();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, ReconnectProducerWhileTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  // Disconnecting the producer while tracing should trigger data source
  // teardown.
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  EXPECT_CALL(mock_producer, OnDisconnect());
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  producer_endpoint.reset();
  task_runner.RunUntilIdle();

  // Reconnecting a producer with a matching data source should see that data
  // source getting enabled.
  EXPECT_CALL(mock_producer, OnConnect());
  producer_endpoint = svc->ConnectProducer(&mock_producer, 123u /* uid */);
  task_runner.RunUntilIdle();
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint->DisableTracing();
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  producer_endpoint.reset();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, ProducerIDWrapping) {
  base::TestTaskRunner task_runner;
  auto shm_factory =
      std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
  std::unique_ptr<ServiceImpl> svc(static_cast<ServiceImpl*>(
      Service::CreateInstance(std::move(shm_factory), &task_runner).release()));

  std::map<ProducerID, std::pair<std::unique_ptr<MockProducer>,
                                 std::unique_ptr<Service::ProducerEndpoint>>>
      producers;

  auto ConnectProducerAndWait = [&task_runner, &svc, &producers]() {
    char checkpoint_name[32];
    static int checkpoint_num = 0;
    sprintf(checkpoint_name, "on_connect_%d", checkpoint_num++);
    auto on_connect = task_runner.CreateCheckpoint(checkpoint_name);
    std::unique_ptr<MockProducer> producer(new MockProducer());
    std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
        svc->ConnectProducer(producer.get(), 123u /* uid */);
    EXPECT_CALL(*producer, OnConnect()).WillOnce(Invoke(on_connect));
    task_runner.RunUntilCheckpoint(checkpoint_name);
    EXPECT_EQ(&*producer_endpoint, svc->GetProducer(svc->last_producer_id_));
    const ProducerID pr_id = svc->last_producer_id_;
    producers.emplace(pr_id, std::make_pair(std::move(producer),
                                            std::move(producer_endpoint)));
    return pr_id;
  };

  auto DisconnectProducerAndWait = [&task_runner,
                                    &producers](ProducerID pr_id) {
    char checkpoint_name[32];
    static int checkpoint_num = 0;
    sprintf(checkpoint_name, "on_disconnect_%d", checkpoint_num++);
    auto on_disconnect = task_runner.CreateCheckpoint(checkpoint_name);
    auto it = producers.find(pr_id);
    PERFETTO_CHECK(it != producers.end());
    EXPECT_CALL(*it->second.first, OnDisconnect())
        .WillOnce(Invoke(on_disconnect));
    producers.erase(pr_id);
    task_runner.RunUntilCheckpoint(checkpoint_name);
  };

  // Connect producers 1-4.
  for (ProducerID i = 1; i <= 4; i++)
    ASSERT_EQ(i, ConnectProducerAndWait());

  // Disconnect producers 1,3.
  DisconnectProducerAndWait(1);
  DisconnectProducerAndWait(3);

  svc->last_producer_id_ = kMaxProducerID - 1;
  ASSERT_EQ(kMaxProducerID, ConnectProducerAndWait());
  ASSERT_EQ(1u, ConnectProducerAndWait());
  ASSERT_EQ(3u, ConnectProducerAndWait());
  ASSERT_EQ(5u, ConnectProducerAndWait());
  ASSERT_EQ(6u, ConnectProducerAndWait());

  // Disconnect all producers to mute spurious callbacks.
  DisconnectProducerAndWait(kMaxProducerID);
  for (ProducerID i = 1; i <= 6; i++)
    DisconnectProducerAndWait(i);
}

}  // namespace perfetto
<commit_msg>perfetto: Silence test logs am: 5afe501b22 am: f44a5629e9<commit_after>/*
 * Copyright (C) 2017 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 "src/tracing/core/service_impl.h"

#include <string.h>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/producer.h"
#include "perfetto/tracing/core/shared_memory.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/base/test/test_task_runner.h"
#include "src/tracing/test/test_shared_memory.h"

namespace perfetto {
using ::testing::_;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::Mock;

namespace {

class MockProducer : public Producer {
 public:
  ~MockProducer() override {}

  // Producer implementation.
  MOCK_METHOD0(OnConnect, void());
  MOCK_METHOD0(OnDisconnect, void());
  MOCK_METHOD2(CreateDataSourceInstance,
               void(DataSourceInstanceID, const DataSourceConfig&));
  MOCK_METHOD1(TearDownDataSourceInstance, void(DataSourceInstanceID));
  MOCK_METHOD0(OnTracingStart, void());
  MOCK_METHOD0(OnTracingStop, void());
};

class MockConsumer : public Consumer {
 public:
  ~MockConsumer() override {}

  // Consumer implementation.
  MOCK_METHOD0(OnConnect, void());
  MOCK_METHOD0(OnDisconnect, void());

  void OnTraceData(std::vector<TracePacket> packets, bool has_more) override {}
};

}  // namespace

class ServiceImplTest : public testing::Test {
 public:
  ServiceImplTest() {
    auto shm_factory =
        std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
    svc.reset(static_cast<ServiceImpl*>(
        Service::CreateInstance(std::move(shm_factory), &task_runner)
            .release()));
  }

  base::TestTaskRunner task_runner;
  std::unique_ptr<ServiceImpl> svc;
};

TEST_F(ServiceImplTest, RegisterAndUnregister) {
  MockProducer mock_producer_1;
  MockProducer mock_producer_2;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_1 =
      svc->ConnectProducer(&mock_producer_1, 123u /* uid */);
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_2 =
      svc->ConnectProducer(&mock_producer_2, 456u /* uid */);

  ASSERT_TRUE(producer_endpoint_1);
  ASSERT_TRUE(producer_endpoint_2);

  InSequence seq;
  EXPECT_CALL(mock_producer_1, OnConnect());
  EXPECT_CALL(mock_producer_2, OnConnect());
  task_runner.RunUntilIdle();

  ASSERT_EQ(2u, svc->num_producers());
  ASSERT_EQ(producer_endpoint_1.get(), svc->GetProducer(1));
  ASSERT_EQ(producer_endpoint_2.get(), svc->GetProducer(2));
  ASSERT_EQ(123u, svc->GetProducer(1)->uid_);
  ASSERT_EQ(456u, svc->GetProducer(2)->uid_);

  DataSourceDescriptor ds_desc1;
  ds_desc1.set_name("foo");
  producer_endpoint_1->RegisterDataSource(
      ds_desc1, [this, &producer_endpoint_1](DataSourceID id) {
        EXPECT_EQ(1u, id);
        task_runner.PostTask(
            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
                      producer_endpoint_1.get(), id));
      });

  DataSourceDescriptor ds_desc2;
  ds_desc2.set_name("bar");
  producer_endpoint_2->RegisterDataSource(
      ds_desc2, [this, &producer_endpoint_2](DataSourceID id) {
        EXPECT_EQ(1u, id);
        task_runner.PostTask(
            std::bind(&Service::ProducerEndpoint::UnregisterDataSource,
                      producer_endpoint_2.get(), id));
      });

  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer_1, OnDisconnect());
  producer_endpoint_1.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer_1);

  ASSERT_EQ(1u, svc->num_producers());
  ASSERT_EQ(nullptr, svc->GetProducer(1));

  EXPECT_CALL(mock_producer_2, OnDisconnect());
  producer_endpoint_2.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer_2);

  ASSERT_EQ(0u, svc->num_producers());
}
TEST_F(ServiceImplTest, EnableAndDisableTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});

  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint->DisableTracing();
  producer_endpoint.reset();
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, LockdownMode) {
  MockConsumer mock_consumer;
  EXPECT_CALL(mock_consumer, OnConnect());
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  TraceConfig trace_config;
  trace_config.set_lockdown_mode(
      TraceConfig::LockdownModeOperation::LOCKDOWN_SET);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  InSequence seq;

  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, geteuid() + 1 /* uid */);

  MockProducer mock_producer_sameuid;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint_sameuid =
      svc->ConnectProducer(&mock_producer_sameuid, geteuid() /* uid */);

  EXPECT_CALL(mock_producer, OnConnect()).Times(0);
  EXPECT_CALL(mock_producer_sameuid, OnConnect());
  task_runner.RunUntilIdle();

  Mock::VerifyAndClearExpectations(&mock_producer);

  consumer_endpoint->DisableTracing();
  task_runner.RunUntilIdle();

  trace_config.set_lockdown_mode(
      TraceConfig::LockdownModeOperation::LOCKDOWN_CLEAR);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer_sameuid, OnDisconnect());
  EXPECT_CALL(mock_producer, OnConnect());
  producer_endpoint_sameuid =
      svc->ConnectProducer(&mock_producer, geteuid() + 1);

  EXPECT_CALL(mock_producer, OnDisconnect());
  task_runner.RunUntilIdle();
}

TEST_F(ServiceImplTest, DisconnectConsumerWhileTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  // Disconnecting the consumer while tracing should trigger data source
  // teardown.
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  producer_endpoint.reset();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, ReconnectProducerWhileTracing) {
  MockProducer mock_producer;
  std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
      svc->ConnectProducer(&mock_producer, 123u /* uid */);
  MockConsumer mock_consumer;
  std::unique_ptr<Service::ConsumerEndpoint> consumer_endpoint =
      svc->ConnectConsumer(&mock_consumer);

  InSequence seq;
  EXPECT_CALL(mock_producer, OnConnect());
  EXPECT_CALL(mock_consumer, OnConnect());
  task_runner.RunUntilIdle();

  DataSourceDescriptor ds_desc;
  ds_desc.set_name("foo");
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  // Disconnecting the producer while tracing should trigger data source
  // teardown.
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  EXPECT_CALL(mock_producer, OnDisconnect());
  TraceConfig trace_config;
  trace_config.add_buffers()->set_size_kb(4096 * 10);
  auto* ds_config = trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("foo");
  ds_config->set_target_buffer(0);
  consumer_endpoint->EnableTracing(trace_config);
  producer_endpoint.reset();
  task_runner.RunUntilIdle();

  // Reconnecting a producer with a matching data source should see that data
  // source getting enabled.
  EXPECT_CALL(mock_producer, OnConnect());
  producer_endpoint = svc->ConnectProducer(&mock_producer, 123u /* uid */);
  task_runner.RunUntilIdle();
  EXPECT_CALL(mock_producer, CreateDataSourceInstance(_, _));
  EXPECT_CALL(mock_producer, TearDownDataSourceInstance(_));
  producer_endpoint->RegisterDataSource(ds_desc, [](DataSourceID) {});
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_consumer, OnDisconnect());
  consumer_endpoint->DisableTracing();
  consumer_endpoint.reset();
  task_runner.RunUntilIdle();

  EXPECT_CALL(mock_producer, OnDisconnect());
  producer_endpoint.reset();
  Mock::VerifyAndClearExpectations(&mock_producer);
  Mock::VerifyAndClearExpectations(&mock_consumer);
}

TEST_F(ServiceImplTest, ProducerIDWrapping) {
  base::TestTaskRunner task_runner;
  auto shm_factory =
      std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
  std::unique_ptr<ServiceImpl> svc(static_cast<ServiceImpl*>(
      Service::CreateInstance(std::move(shm_factory), &task_runner).release()));

  std::map<ProducerID, std::pair<std::unique_ptr<MockProducer>,
                                 std::unique_ptr<Service::ProducerEndpoint>>>
      producers;

  auto ConnectProducerAndWait = [&task_runner, &svc, &producers]() {
    char checkpoint_name[32];
    static int checkpoint_num = 0;
    sprintf(checkpoint_name, "on_connect_%d", checkpoint_num++);
    auto on_connect = task_runner.CreateCheckpoint(checkpoint_name);
    std::unique_ptr<MockProducer> producer(new MockProducer());
    std::unique_ptr<Service::ProducerEndpoint> producer_endpoint =
        svc->ConnectProducer(producer.get(), 123u /* uid */);
    EXPECT_CALL(*producer, OnConnect()).WillOnce(Invoke(on_connect));
    task_runner.RunUntilCheckpoint(checkpoint_name);
    EXPECT_EQ(&*producer_endpoint, svc->GetProducer(svc->last_producer_id_));
    const ProducerID pr_id = svc->last_producer_id_;
    producers.emplace(pr_id, std::make_pair(std::move(producer),
                                            std::move(producer_endpoint)));
    return pr_id;
  };

  auto DisconnectProducerAndWait = [&task_runner,
                                    &producers](ProducerID pr_id) {
    char checkpoint_name[32];
    static int checkpoint_num = 0;
    sprintf(checkpoint_name, "on_disconnect_%d", checkpoint_num++);
    auto on_disconnect = task_runner.CreateCheckpoint(checkpoint_name);
    auto it = producers.find(pr_id);
    PERFETTO_CHECK(it != producers.end());
    EXPECT_CALL(*it->second.first, OnDisconnect())
        .WillOnce(Invoke(on_disconnect));
    producers.erase(pr_id);
    task_runner.RunUntilCheckpoint(checkpoint_name);
  };

  // Connect producers 1-4.
  for (ProducerID i = 1; i <= 4; i++)
    ASSERT_EQ(i, ConnectProducerAndWait());

  // Disconnect producers 1,3.
  DisconnectProducerAndWait(1);
  DisconnectProducerAndWait(3);

  svc->last_producer_id_ = kMaxProducerID - 1;
  ASSERT_EQ(kMaxProducerID, ConnectProducerAndWait());
  ASSERT_EQ(1u, ConnectProducerAndWait());
  ASSERT_EQ(3u, ConnectProducerAndWait());
  ASSERT_EQ(5u, ConnectProducerAndWait());
  ASSERT_EQ(6u, ConnectProducerAndWait());

  // Disconnect all producers to mute spurious callbacks.
  DisconnectProducerAndWait(kMaxProducerID);
  for (ProducerID i = 1; i <= 6; i++)
    DisconnectProducerAndWait(i);
}

}  // namespace perfetto
<|endoftext|>


<commit_before>//===-- llvm-diff.cpp - Module comparator command-line driver ---*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the command-line driver for the difference engine.
//
//===----------------------------------------------------------------------===//

#include "DifferenceEngine.h"

#include "llvm/Instructions.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/SourceMgr.h"

#include <string>
#include <utility>

using namespace llvm;

/// Reads a module from a file.  If the filename ends in .ll, it is
/// interpreted as an assembly file;  otherwise, it is interpreted as
/// bitcode.  On error, messages are written to stderr and null is
/// returned.
static Module *ReadModule(LLVMContext &Context, StringRef Name) {
  // LLVM assembly path.
  if (Name.endswith(".ll")) {
    SMDiagnostic Diag;
    Module *M = ParseAssemblyFile(Name, Diag, Context);
    if (M) return M;

    Diag.Print("llvmdiff", errs());
    return 0;
  }

  // Bitcode path.
  MemoryBuffer *Buffer = MemoryBuffer::getFile(Name);

  // ParseBitcodeFile takes ownership of the buffer if it succeeds.
  std::string Error;
  Module *M = ParseBitcodeFile(Buffer, Context, &Error);
  if (M) return M;

  errs() << "error parsing " << Name << ": " << Error;
  delete Buffer;
  return 0;
}

namespace {
struct DiffContext {
  DiffContext(Value *L, Value *R)
    : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {}
  Value *L;
  Value *R;
  bool Differences;
  bool IsFunction;
  DenseMap<Value*,unsigned> LNumbering;
  DenseMap<Value*,unsigned> RNumbering;
};

void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering) {
  unsigned BBN = 0;
  unsigned IN = 0;

  // Arguments get the first numbers.
  for (Function::arg_iterator
         AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI)
    if (!AI->hasName())
      Numbering[&*AI] = IN++;

  // Walk the basic blocks in order.
  for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) {
    // Basic blocks have their own 'namespace'.
    if (!FI->hasName())
      Numbering[&*FI] = BBN++;

    // Walk the instructions in order.
    for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI)
      // void instructions don't get numbers.
      if (!BI->hasName() && !BI->getType()->isVoidTy())
        Numbering[&*BI] = IN++;
  }

  assert(!Numbering.empty() && "asked for numbering but numbering was no-op");
}

class DiffConsumer : public DifferenceEngine::Consumer {
private:
  raw_ostream &out;
  Module *LModule;
  Module *RModule;
  SmallVector<DiffContext, 5> contexts;
  bool Differences;
  unsigned Indent;

  void printValue(Value *V, bool isL) {
    if (V->hasName()) {
      out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName();
      return;
    }
    if (V->getType()->isVoidTy()) {
      if (isa<StoreInst>(V)) {
        out << "store to ";
        printValue(cast<StoreInst>(V)->getPointerOperand(), isL);
      } else if (isa<CallInst>(V)) {
        out << "call to ";
        printValue(cast<CallInst>(V)->getCalledValue(), isL);
      } else if (isa<InvokeInst>(V)) {
        out << "invoke to ";
        printValue(cast<InvokeInst>(V)->getCalledValue(), isL);
      } else {
        out << *V;
      }
      return;
    }

    unsigned N = contexts.size();
    while (N > 0) {
      --N;
      DiffContext &ctxt = contexts[N];
      if (!ctxt.IsFunction) continue;
      if (isL) {
        if (ctxt.LNumbering.empty())
          ComputeNumbering(cast<Function>(ctxt.L), ctxt.LNumbering);
        out << '%' << ctxt.LNumbering[V];
        return;
      } else {
        if (ctxt.RNumbering.empty())
          ComputeNumbering(cast<Function>(ctxt.R), ctxt.RNumbering);
        out << '%' << ctxt.RNumbering[V];
        return;
      }
    }

    out << "<anonymous>";
  }

  void header() {
    if (contexts.empty()) return;
    for (SmallVectorImpl<DiffContext>::iterator
           I = contexts.begin(), E = contexts.end(); I != E; ++I) {
      if (I->Differences) continue;
      if (isa<Function>(I->L)) {
        // Extra newline between functions.
        if (Differences) out << "\n";

        Function *L = cast<Function>(I->L);
        Function *R = cast<Function>(I->R);
        if (L->getName() != R->getName())
          out << "in function " << L->getName() << " / " << R->getName() << ":\n";
        else
          out << "in function " << L->getName() << ":\n";
      } else if (isa<BasicBlock>(I->L)) {
        BasicBlock *L = cast<BasicBlock>(I->L);
        BasicBlock *R = cast<BasicBlock>(I->R);
        out << "  in block ";
        printValue(L, true);
        out << " / ";
        printValue(R, false);
        out << ":\n";
      } else if (isa<Instruction>(I->L)) {
        out << "    in instruction ";
        printValue(I->L, true);
        out << " / ";
        printValue(I->R, false);
        out << ":\n";
      }

      I->Differences = true;
    }
  }

  void indent() {
    unsigned N = Indent;
    while (N--) out << ' ';
  }

public:
  DiffConsumer(Module *L, Module *R)
    : out(errs()), LModule(L), RModule(R), Differences(false), Indent(0) {}

  bool hadDifferences() const { return Differences; }

  void enterContext(Value *L, Value *R) {
    contexts.push_back(DiffContext(L, R));
    Indent += 2;
  }
  void exitContext() {
    Differences |= contexts.back().Differences;
    contexts.pop_back();
    Indent -= 2;
  }

  void log(StringRef text) {
    header();
    indent();
    out << text << '\n';
  }

  void logf(const DifferenceEngine::LogBuilder &Log) {
    header();
    indent();

    unsigned arg = 0;

    StringRef format = Log.getFormat();
    while (true) {
      size_t percent = format.find('%');
      if (percent == StringRef::npos) {
        out << format;
        break;
      }
      assert(format[percent] == '%');

      if (percent > 0) out << format.substr(0, percent);

      switch (format[percent+1]) {
      case '%': out << '%'; break;
      case 'l': printValue(Log.getArgument(arg++), true); break;
      case 'r': printValue(Log.getArgument(arg++), false); break;
      default: llvm_unreachable("unknown format character");
      }

      format = format.substr(percent+2);
    }

    out << '\n';
  }

  void logd(const DifferenceEngine::DiffLogBuilder &Log) {
    header();

    for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) {
      indent();
      switch (Log.getLineKind(I)) {
      case DifferenceEngine::DC_match:
        out << "  ";
        Log.getLeft(I)->dump();
        //printValue(Log.getLeft(I), true);
        break;
      case DifferenceEngine::DC_left:
        out << "< ";
        Log.getLeft(I)->dump();
        //printValue(Log.getLeft(I), true);
        break;
      case DifferenceEngine::DC_right:
        out << "> ";
        Log.getRight(I)->dump();
        //printValue(Log.getRight(I), false);
        break;
      }
      //out << "\n";
    }
  }

};
}

static void diffGlobal(DifferenceEngine &Engine, Module *L, Module *R,
                       StringRef Name) {
  // Drop leading sigils from the global name.
  if (Name.startswith("@")) Name = Name.substr(1);

  Function *LFn = L->getFunction(Name);
  Function *RFn = R->getFunction(Name);
  if (LFn && RFn)
    Engine.diff(LFn, RFn);
  else if (!LFn && !RFn)
    errs() << "No function named @" << Name << " in either module\n";
  else if (!LFn)
    errs() << "No function named @" << Name << " in left module\n";
  else
    errs() << "No function named @" << Name << " in right module\n";
}

cl::opt<std::string> LeftFilename(cl::Positional, cl::desc("<first file>"), cl::Required);
cl::opt<std::string> RightFilename(cl::Positional, cl::desc("<second file>"), cl::Required);
cl::list<std::string> GlobalsToCompare(cl::Positional, cl::desc("<globals to compare>"));

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);

  LLVMContext Context;

  // Load both modules.  Die if that fails.
  Module *LModule = ReadModule(Context, LeftFilename);
  Module *RModule = ReadModule(Context, RightFilename);
  if (!LModule || !RModule) return 1;

  DiffConsumer Consumer(LModule, RModule);
  DifferenceEngine Engine(Context, Consumer);

  // If any global names were given, just diff those.
  if (!GlobalsToCompare.empty()) {
    for (unsigned I = 0, E = GlobalsToCompare.size(); I != E; ++I)
      diffGlobal(Engine, LModule, RModule, GlobalsToCompare[I]);

  // Otherwise, diff everything in the module.
  } else {
    Engine.diff(LModule, RModule);
  }

  delete LModule;
  delete RModule;

  return Consumer.hadDifferences();
}
<commit_msg>Transcribe IRC to svn.  Also don't print basic block names twice if they match.<commit_after>//===-- llvm-diff.cpp - Module comparator command-line driver ---*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the command-line driver for the difference engine.
//
//===----------------------------------------------------------------------===//

#include "DifferenceEngine.h"

#include "llvm/Instructions.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/SourceMgr.h"

#include <string>
#include <utility>

using namespace llvm;

/// Reads a module from a file.  If the filename ends in .ll, it is
/// interpreted as an assembly file;  otherwise, it is interpreted as
/// bitcode.  On error, messages are written to stderr and null is
/// returned.
static Module *ReadModule(LLVMContext &Context, StringRef Name) {
  // LLVM assembly path.
  if (Name.endswith(".ll")) {
    SMDiagnostic Diag;
    Module *M = ParseAssemblyFile(Name, Diag, Context);
    if (M) return M;

    Diag.Print("llvmdiff", errs());
    return 0;
  }

  // Bitcode path.
  MemoryBuffer *Buffer = MemoryBuffer::getFile(Name);

  // ParseBitcodeFile takes ownership of the buffer if it succeeds.
  std::string Error;
  Module *M = ParseBitcodeFile(Buffer, Context, &Error);
  if (M) return M;

  errs() << "error parsing " << Name << ": " << Error;
  delete Buffer;
  return 0;
}

namespace {
struct DiffContext {
  DiffContext(Value *L, Value *R)
    : L(L), R(R), Differences(false), IsFunction(isa<Function>(L)) {}
  Value *L;
  Value *R;
  bool Differences;
  bool IsFunction;
  DenseMap<Value*,unsigned> LNumbering;
  DenseMap<Value*,unsigned> RNumbering;
};

void ComputeNumbering(Function *F, DenseMap<Value*,unsigned> &Numbering) {
  unsigned BBN = 0;
  unsigned IN = 0;

  // Arguments get the first numbers.
  for (Function::arg_iterator
         AI = F->arg_begin(), AE = F->arg_end(); AI != AE; ++AI)
    if (!AI->hasName())
      Numbering[&*AI] = IN++;

  // Walk the basic blocks in order.
  for (Function::iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI) {
    // Basic blocks have their own 'namespace'.
    if (!FI->hasName())
      Numbering[&*FI] = BBN++;

    // Walk the instructions in order.
    for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ++BI)
      // void instructions don't get numbers.
      if (!BI->hasName() && !BI->getType()->isVoidTy())
        Numbering[&*BI] = IN++;
  }

  assert(!Numbering.empty() && "asked for numbering but numbering was no-op");
}

class DiffConsumer : public DifferenceEngine::Consumer {
private:
  raw_ostream &out;
  Module *LModule;
  Module *RModule;
  SmallVector<DiffContext, 5> contexts;
  bool Differences;
  unsigned Indent;

  void printValue(Value *V, bool isL) {
    if (V->hasName()) {
      out << (isa<GlobalValue>(V) ? '@' : '%') << V->getName();
      return;
    }
    if (V->getType()->isVoidTy()) {
      if (isa<StoreInst>(V)) {
        out << "store to ";
        printValue(cast<StoreInst>(V)->getPointerOperand(), isL);
      } else if (isa<CallInst>(V)) {
        out << "call to ";
        printValue(cast<CallInst>(V)->getCalledValue(), isL);
      } else if (isa<InvokeInst>(V)) {
        out << "invoke to ";
        printValue(cast<InvokeInst>(V)->getCalledValue(), isL);
      } else {
        out << *V;
      }
      return;
    }

    unsigned N = contexts.size();
    while (N > 0) {
      --N;
      DiffContext &ctxt = contexts[N];
      if (!ctxt.IsFunction) continue;
      if (isL) {
        if (ctxt.LNumbering.empty())
          ComputeNumbering(cast<Function>(ctxt.L), ctxt.LNumbering);
        out << '%' << ctxt.LNumbering[V];
        return;
      } else {
        if (ctxt.RNumbering.empty())
          ComputeNumbering(cast<Function>(ctxt.R), ctxt.RNumbering);
        out << '%' << ctxt.RNumbering[V];
        return;
      }
    }

    out << "<anonymous>";
  }

  void header() {
    if (contexts.empty()) return;
    for (SmallVectorImpl<DiffContext>::iterator
           I = contexts.begin(), E = contexts.end(); I != E; ++I) {
      if (I->Differences) continue;
      if (isa<Function>(I->L)) {
        // Extra newline between functions.
        if (Differences) out << "\n";

        Function *L = cast<Function>(I->L);
        Function *R = cast<Function>(I->R);
        if (L->getName() != R->getName())
          out << "in function " << L->getName()
              << " / " << R->getName() << ":\n";
        else
          out << "in function " << L->getName() << ":\n";
      } else if (isa<BasicBlock>(I->L)) {
        BasicBlock *L = cast<BasicBlock>(I->L);
        BasicBlock *R = cast<BasicBlock>(I->R);
        if (L->hasName() && R->hasName() && L->getName() == R->getName())
          out << "  in block %" << L->getName() << ":\n";
        else {
          out << "  in block ";
          printValue(L, true);
          out << " / ";
          printValue(R, false);
          out << ":\n";
        }
      } else if (isa<Instruction>(I->L)) {
        out << "    in instruction ";
        printValue(I->L, true);
        out << " / ";
        printValue(I->R, false);
        out << ":\n";
      }

      I->Differences = true;
    }
  }

  void indent() {
    unsigned N = Indent;
    while (N--) out << ' ';
  }

public:
  DiffConsumer(Module *L, Module *R)
    : out(errs()), LModule(L), RModule(R), Differences(false), Indent(0) {}

  bool hadDifferences() const { return Differences; }

  void enterContext(Value *L, Value *R) {
    contexts.push_back(DiffContext(L, R));
    Indent += 2;
  }
  void exitContext() {
    Differences |= contexts.back().Differences;
    contexts.pop_back();
    Indent -= 2;
  }

  void log(StringRef text) {
    header();
    indent();
    out << text << '\n';
  }

  void logf(const DifferenceEngine::LogBuilder &Log) {
    header();
    indent();

    unsigned arg = 0;

    StringRef format = Log.getFormat();
    while (true) {
      size_t percent = format.find('%');
      if (percent == StringRef::npos) {
        out << format;
        break;
      }
      assert(format[percent] == '%');

      if (percent > 0) out << format.substr(0, percent);

      switch (format[percent+1]) {
      case '%': out << '%'; break;
      case 'l': printValue(Log.getArgument(arg++), true); break;
      case 'r': printValue(Log.getArgument(arg++), false); break;
      default: llvm_unreachable("unknown format character");
      }

      format = format.substr(percent+2);
    }

    out << '\n';
  }

  void logd(const DifferenceEngine::DiffLogBuilder &Log) {
    header();

    for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) {
      indent();
      switch (Log.getLineKind(I)) {
      case DifferenceEngine::DC_match:
        out << "  ";
        Log.getLeft(I)->dump();
        //printValue(Log.getLeft(I), true);
        break;
      case DifferenceEngine::DC_left:
        out << "< ";
        Log.getLeft(I)->dump();
        //printValue(Log.getLeft(I), true);
        break;
      case DifferenceEngine::DC_right:
        out << "> ";
        Log.getRight(I)->dump();
        //printValue(Log.getRight(I), false);
        break;
      }
      //out << "\n";
    }
  }

};
}

static void diffGlobal(DifferenceEngine &Engine, Module *L, Module *R,
                       StringRef Name) {
  // Drop leading sigils from the global name.
  if (Name.startswith("@")) Name = Name.substr(1);

  Function *LFn = L->getFunction(Name);
  Function *RFn = R->getFunction(Name);
  if (LFn && RFn)
    Engine.diff(LFn, RFn);
  else if (!LFn && !RFn)
    errs() << "No function named @" << Name << " in either module\n";
  else if (!LFn)
    errs() << "No function named @" << Name << " in left module\n";
  else
    errs() << "No function named @" << Name << " in right module\n";
}

cl::opt<std::string> LeftFilename(cl::Positional,
                                  cl::desc("<first file>"),
                                  cl::Required);
cl::opt<std::string> RightFilename(cl::Positional,
                                   cl::desc("<second file>"),
                                   cl::Required);
cl::list<std::string> GlobalsToCompare(cl::Positional,
                                       cl::desc("<globals to compare>"));

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);

  LLVMContext Context;

  // Load both modules.  Die if that fails.
  Module *LModule = ReadModule(Context, LeftFilename);
  Module *RModule = ReadModule(Context, RightFilename);
  if (!LModule || !RModule) return 1;

  DiffConsumer Consumer(LModule, RModule);
  DifferenceEngine Engine(Context, Consumer);

  // If any global names were given, just diff those.
  if (!GlobalsToCompare.empty()) {
    for (unsigned I = 0, E = GlobalsToCompare.size(); I != E; ++I)
      diffGlobal(Engine, LModule, RModule, GlobalsToCompare[I]);

  // Otherwise, diff everything in the module.
  } else {
    Engine.diff(LModule, RModule);
  }

  delete LModule;
  delete RModule;

  return Consumer.hadDifferences();
}
<|endoftext|>


<commit_before>//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program takes in a list of bitcode files, links them and performs
// link-time optimization according to the provided symbol resolutions using the
// resolution-based LTO interface, and outputs one or more object files.
//
// This program is intended to eventually replace llvm-lto which uses the legacy
// LTO interface.
//
//===----------------------------------------------------------------------===//

#include "llvm/LTO/Caching.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"

using namespace llvm;
using namespace lto;
using namespace object;

static cl::opt<char>
    OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
                           "(default = '-O2')"),
             cl::Prefix, cl::ZeroOrMore, cl::init('2'));

static cl::opt<char> CGOptLevel(
    "cg-opt-level",
    cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
    cl::init('2'));

static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
                                            cl::desc("<input bitcode files>"));

static cl::opt<std::string> OutputFilename("o", cl::Required,
                                           cl::desc("Output filename"),
                                           cl::value_desc("filename"));

static cl::opt<std::string> CacheDir("cache-dir", cl::desc("Cache Directory"),
                                     cl::value_desc("directory"));

static cl::opt<std::string> OptPipeline("opt-pipeline",
                                        cl::desc("Optimizer Pipeline"),
                                        cl::value_desc("pipeline"));

static cl::opt<std::string> AAPipeline("aa-pipeline",
                                       cl::desc("Alias Analysis Pipeline"),
                                       cl::value_desc("aapipeline"));

static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));

static cl::opt<bool>
    ThinLTODistributedIndexes("thinlto-distributed-indexes", cl::init(false),
                              cl::desc("Write out individual index and "
                                       "import files for the "
                                       "distributed backend case"));

static cl::opt<int> Threads("-thinlto-threads",
                            cl::init(llvm::heavyweight_hardware_concurrency()));

static cl::list<std::string> SymbolResolutions(
    "r",
    cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
             "where \"resolution\" is a sequence (which may be empty) of the\n"
             "following characters:\n"
             " p - prevailing: the linker has chosen this definition of the\n"
             "     symbol\n"
             " l - local: the definition of this symbol is unpreemptable at\n"
             "     runtime and is known to be in this linkage unit\n"
             " x - externally visible: the definition of this symbol is\n"
             "     visible outside of the LTO unit\n"
             "A resolution for each symbol must be specified."),
    cl::ZeroOrMore);

static cl::opt<std::string> OverrideTriple(
    "override-triple",
    cl::desc("Replace target triples in input files with this triple"));

static cl::opt<std::string> DefaultTriple(
    "default-triple",
    cl::desc(
        "Replace unspecified target triples in input files with this triple"));

static void check(Error E, std::string Msg) {
  if (!E)
    return;
  handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
    errs() << "llvm-lto: " << Msg << ": " << EIB.message().c_str() << '\n';
  });
  exit(1);
}

template <typename T> static T check(Expected<T> E, std::string Msg) {
  if (E)
    return std::move(*E);
  check(E.takeError(), Msg);
  return T();
}

static void check(std::error_code EC, std::string Msg) {
  check(errorCodeToError(EC), Msg);
}

template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
  if (E)
    return std::move(*E);
  check(E.getError(), Msg);
  return T();
}

int main(int argc, char **argv) {
  InitializeAllTargets();
  InitializeAllTargetMCs();
  InitializeAllAsmPrinters();
  InitializeAllAsmParsers();

  cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");

  // FIXME: Workaround PR30396 which means that a symbol can appear
  // more than once if it is defined in module-level assembly and
  // has a GV declaration. We allow (file, symbol) pairs to have multiple
  // resolutions and apply them in the order observed.
  std::map<std::pair<std::string, std::string>, std::list<SymbolResolution>>
      CommandLineResolutions;
  for (std::string R : SymbolResolutions) {
    StringRef Rest = R;
    StringRef FileName, SymbolName;
    std::tie(FileName, Rest) = Rest.split(',');
    if (Rest.empty()) {
      llvm::errs() << "invalid resolution: " << R << '\n';
      return 1;
    }
    std::tie(SymbolName, Rest) = Rest.split(',');
    SymbolResolution Res;
    for (char C : Rest) {
      if (C == 'p')
        Res.Prevailing = true;
      else if (C == 'l')
        Res.FinalDefinitionInLinkageUnit = true;
      else if (C == 'x')
        Res.VisibleToRegularObj = true;
      else
        llvm::errs() << "invalid character " << C << " in resolution: " << R
                     << '\n';
    }
    CommandLineResolutions[{FileName, SymbolName}].push_back(Res);
  }

  std::vector<std::unique_ptr<MemoryBuffer>> MBs;

  Config Conf;
  Conf.DiagHandler = [](const DiagnosticInfo &) {
    exit(1);
  };

  Conf.CPU = MCPU;
  Conf.Options = InitTargetOptionsFromCodeGenFlags();
  Conf.MAttrs = MAttrs;
  if (auto RM = getRelocModel())
    Conf.RelocModel = *RM;
  Conf.CodeModel = CMModel;

  if (SaveTemps)
    check(Conf.addSaveTemps(OutputFilename + "."),
          "Config::addSaveTemps failed");

  // Run a custom pipeline, if asked for.
  Conf.OptPipeline = OptPipeline;
  Conf.AAPipeline = AAPipeline;

  Conf.OptLevel = OptLevel - '0';
  switch (CGOptLevel) {
  case '0':
    Conf.CGOptLevel = CodeGenOpt::None;
    break;
  case '1':
    Conf.CGOptLevel = CodeGenOpt::Less;
    break;
  case '2':
    Conf.CGOptLevel = CodeGenOpt::Default;
    break;
  case '3':
    Conf.CGOptLevel = CodeGenOpt::Aggressive;
    break;
  default:
    llvm::errs() << "invalid cg optimization level: " << CGOptLevel << '\n';
    return 1;
  }

  Conf.OverrideTriple = OverrideTriple;
  Conf.DefaultTriple = DefaultTriple;

  ThinBackend Backend;
  if (ThinLTODistributedIndexes)
    Backend = createWriteIndexesThinBackend("", "", true, "");
  else
    Backend = createInProcessThinBackend(Threads);
  LTO Lto(std::move(Conf), std::move(Backend));

  bool HasErrors = false;
  for (std::string F : InputFilenames) {
    std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
    std::unique_ptr<InputFile> Input =
        check(InputFile::create(MB->getMemBufferRef()), F);

    std::vector<SymbolResolution> Res;
    for (const InputFile::Symbol &Sym : Input->symbols()) {
      auto I = CommandLineResolutions.find({F, Sym.getName()});
      if (I == CommandLineResolutions.end()) {
        llvm::errs() << argv[0] << ": missing symbol resolution for " << F
                     << ',' << Sym.getName() << '\n';
        HasErrors = true;
      } else {
        Res.push_back(I->second.front());
        I->second.pop_front();
        if (I->second.empty())
          CommandLineResolutions.erase(I);
      }
    }

    if (HasErrors)
      continue;

    MBs.push_back(std::move(MB));
    check(Lto.add(std::move(Input), Res), F);
  }

  if (!CommandLineResolutions.empty()) {
    HasErrors = true;
    for (auto UnusedRes : CommandLineResolutions)
      llvm::errs() << argv[0] << ": unused symbol resolution for "
                   << UnusedRes.first.first << ',' << UnusedRes.first.second
                   << '\n';
  }
  if (HasErrors)
    return 1;

  auto AddStream =
      [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
    std::string Path = OutputFilename + "." + utostr(Task);

    std::error_code EC;
    auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
    check(EC, Path);
    return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
  };

  auto AddFile = [&](size_t Task, StringRef Path) {
    auto ReloadedBufferOrErr = MemoryBuffer::getFile(Path);
    if (auto EC = ReloadedBufferOrErr.getError())
      report_fatal_error(Twine("Can't reload cached file '") + Path + "': " +
                         EC.message() + "\n");

    *AddStream(Task)->OS << (*ReloadedBufferOrErr)->getBuffer();
  };

  NativeObjectCache Cache;
  if (!CacheDir.empty())
    Cache = localCache(CacheDir, AddFile);

  check(Lto.run(AddStream, Cache), "LTO::run failed");
}
<commit_msg>llvm-lto2: Print diagnostics before exiting (NFC)<commit_after>//===-- llvm-lto2: test harness for the resolution-based LTO interface ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This program takes in a list of bitcode files, links them and performs
// link-time optimization according to the provided symbol resolutions using the
// resolution-based LTO interface, and outputs one or more object files.
//
// This program is intended to eventually replace llvm-lto which uses the legacy
// LTO interface.
//
//===----------------------------------------------------------------------===//

#include "llvm/LTO/Caching.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Threading.h"

using namespace llvm;
using namespace lto;
using namespace object;

static cl::opt<char>
    OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] "
                           "(default = '-O2')"),
             cl::Prefix, cl::ZeroOrMore, cl::init('2'));

static cl::opt<char> CGOptLevel(
    "cg-opt-level",
    cl::desc("Codegen optimization level (0, 1, 2 or 3, default = '2')"),
    cl::init('2'));

static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore,
                                            cl::desc("<input bitcode files>"));

static cl::opt<std::string> OutputFilename("o", cl::Required,
                                           cl::desc("Output filename"),
                                           cl::value_desc("filename"));

static cl::opt<std::string> CacheDir("cache-dir", cl::desc("Cache Directory"),
                                     cl::value_desc("directory"));

static cl::opt<std::string> OptPipeline("opt-pipeline",
                                        cl::desc("Optimizer Pipeline"),
                                        cl::value_desc("pipeline"));

static cl::opt<std::string> AAPipeline("aa-pipeline",
                                       cl::desc("Alias Analysis Pipeline"),
                                       cl::value_desc("aapipeline"));

static cl::opt<bool> SaveTemps("save-temps", cl::desc("Save temporary files"));

static cl::opt<bool>
    ThinLTODistributedIndexes("thinlto-distributed-indexes", cl::init(false),
                              cl::desc("Write out individual index and "
                                       "import files for the "
                                       "distributed backend case"));

static cl::opt<int> Threads("-thinlto-threads",
                            cl::init(llvm::heavyweight_hardware_concurrency()));

static cl::list<std::string> SymbolResolutions(
    "r",
    cl::desc("Specify a symbol resolution: filename,symbolname,resolution\n"
             "where \"resolution\" is a sequence (which may be empty) of the\n"
             "following characters:\n"
             " p - prevailing: the linker has chosen this definition of the\n"
             "     symbol\n"
             " l - local: the definition of this symbol is unpreemptable at\n"
             "     runtime and is known to be in this linkage unit\n"
             " x - externally visible: the definition of this symbol is\n"
             "     visible outside of the LTO unit\n"
             "A resolution for each symbol must be specified."),
    cl::ZeroOrMore);

static cl::opt<std::string> OverrideTriple(
    "override-triple",
    cl::desc("Replace target triples in input files with this triple"));

static cl::opt<std::string> DefaultTriple(
    "default-triple",
    cl::desc(
        "Replace unspecified target triples in input files with this triple"));

static void check(Error E, std::string Msg) {
  if (!E)
    return;
  handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) {
    errs() << "llvm-lto: " << Msg << ": " << EIB.message().c_str() << '\n';
  });
  exit(1);
}

template <typename T> static T check(Expected<T> E, std::string Msg) {
  if (E)
    return std::move(*E);
  check(E.takeError(), Msg);
  return T();
}

static void check(std::error_code EC, std::string Msg) {
  check(errorCodeToError(EC), Msg);
}

template <typename T> static T check(ErrorOr<T> E, std::string Msg) {
  if (E)
    return std::move(*E);
  check(E.getError(), Msg);
  return T();
}

int main(int argc, char **argv) {
  InitializeAllTargets();
  InitializeAllTargetMCs();
  InitializeAllAsmPrinters();
  InitializeAllAsmParsers();

  cl::ParseCommandLineOptions(argc, argv, "Resolution-based LTO test harness");

  // FIXME: Workaround PR30396 which means that a symbol can appear
  // more than once if it is defined in module-level assembly and
  // has a GV declaration. We allow (file, symbol) pairs to have multiple
  // resolutions and apply them in the order observed.
  std::map<std::pair<std::string, std::string>, std::list<SymbolResolution>>
      CommandLineResolutions;
  for (std::string R : SymbolResolutions) {
    StringRef Rest = R;
    StringRef FileName, SymbolName;
    std::tie(FileName, Rest) = Rest.split(',');
    if (Rest.empty()) {
      llvm::errs() << "invalid resolution: " << R << '\n';
      return 1;
    }
    std::tie(SymbolName, Rest) = Rest.split(',');
    SymbolResolution Res;
    for (char C : Rest) {
      if (C == 'p')
        Res.Prevailing = true;
      else if (C == 'l')
        Res.FinalDefinitionInLinkageUnit = true;
      else if (C == 'x')
        Res.VisibleToRegularObj = true;
      else
        llvm::errs() << "invalid character " << C << " in resolution: " << R
                     << '\n';
    }
    CommandLineResolutions[{FileName, SymbolName}].push_back(Res);
  }

  std::vector<std::unique_ptr<MemoryBuffer>> MBs;

  Config Conf;
  Conf.DiagHandler = [](const DiagnosticInfo &DI) {
    DiagnosticPrinterRawOStream DP(errs());
    DI.print(DP);
    errs() << '\n';
    exit(1);
  };

  Conf.CPU = MCPU;
  Conf.Options = InitTargetOptionsFromCodeGenFlags();
  Conf.MAttrs = MAttrs;
  if (auto RM = getRelocModel())
    Conf.RelocModel = *RM;
  Conf.CodeModel = CMModel;

  if (SaveTemps)
    check(Conf.addSaveTemps(OutputFilename + "."),
          "Config::addSaveTemps failed");

  // Run a custom pipeline, if asked for.
  Conf.OptPipeline = OptPipeline;
  Conf.AAPipeline = AAPipeline;

  Conf.OptLevel = OptLevel - '0';
  switch (CGOptLevel) {
  case '0':
    Conf.CGOptLevel = CodeGenOpt::None;
    break;
  case '1':
    Conf.CGOptLevel = CodeGenOpt::Less;
    break;
  case '2':
    Conf.CGOptLevel = CodeGenOpt::Default;
    break;
  case '3':
    Conf.CGOptLevel = CodeGenOpt::Aggressive;
    break;
  default:
    llvm::errs() << "invalid cg optimization level: " << CGOptLevel << '\n';
    return 1;
  }

  Conf.OverrideTriple = OverrideTriple;
  Conf.DefaultTriple = DefaultTriple;

  ThinBackend Backend;
  if (ThinLTODistributedIndexes)
    Backend = createWriteIndexesThinBackend("", "", true, "");
  else
    Backend = createInProcessThinBackend(Threads);
  LTO Lto(std::move(Conf), std::move(Backend));

  bool HasErrors = false;
  for (std::string F : InputFilenames) {
    std::unique_ptr<MemoryBuffer> MB = check(MemoryBuffer::getFile(F), F);
    std::unique_ptr<InputFile> Input =
        check(InputFile::create(MB->getMemBufferRef()), F);

    std::vector<SymbolResolution> Res;
    for (const InputFile::Symbol &Sym : Input->symbols()) {
      auto I = CommandLineResolutions.find({F, Sym.getName()});
      if (I == CommandLineResolutions.end()) {
        llvm::errs() << argv[0] << ": missing symbol resolution for " << F
                     << ',' << Sym.getName() << '\n';
        HasErrors = true;
      } else {
        Res.push_back(I->second.front());
        I->second.pop_front();
        if (I->second.empty())
          CommandLineResolutions.erase(I);
      }
    }

    if (HasErrors)
      continue;

    MBs.push_back(std::move(MB));
    check(Lto.add(std::move(Input), Res), F);
  }

  if (!CommandLineResolutions.empty()) {
    HasErrors = true;
    for (auto UnusedRes : CommandLineResolutions)
      llvm::errs() << argv[0] << ": unused symbol resolution for "
                   << UnusedRes.first.first << ',' << UnusedRes.first.second
                   << '\n';
  }
  if (HasErrors)
    return 1;

  auto AddStream =
      [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
    std::string Path = OutputFilename + "." + utostr(Task);

    std::error_code EC;
    auto S = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
    check(EC, Path);
    return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
  };

  auto AddFile = [&](size_t Task, StringRef Path) {
    auto ReloadedBufferOrErr = MemoryBuffer::getFile(Path);
    if (auto EC = ReloadedBufferOrErr.getError())
      report_fatal_error(Twine("Can't reload cached file '") + Path + "': " +
                         EC.message() + "\n");

    *AddStream(Task)->OS << (*ReloadedBufferOrErr)->getBuffer();
  };

  NativeObjectCache Cache;
  if (!CacheDir.empty())
    Cache = localCache(CacheDir, AddFile);

  check(Lto.run(AddStream, Cache), "LTO::run failed");
}
<|endoftext|>


<commit_before>/*******************************************************************************
Copyright (c) 2019, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
	* Redistributions of source code must retain the above copyright
	  notice, this list of conditions and the following disclaimer.
	* Redistributions in binary form must reproduce the above copyright
	  notice, this list of conditions and the following disclaimer in the
	  documentation and/or other materials provided with the distribution.
	* Neither the name of the <organization> nor the
	  names of its contributors may be used to endorse or promote products
	  derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <iostream>

#include <config.h>

#include "connections.h"
#include "eventapi.h"
#include "threadpool.h"
#include "exception.h"

#include "event.h"

bool libhttppp::Event::_Run=true;
bool libhttppp::Event::_Restart=false;

libhttppp::Event::Event(libhttppp::ServerSocket* serversocket) : EVENT(serversocket){
    libhttppp::CtrlHandler::initCtrlHandler();
}

libhttppp::Event::~Event(){
}

libhttppp::EventApi::~EventApi(){
}

void libhttppp::CtrlHandler::CTRLCloseEvent() {
    libhttppp::Event::_Run = false;
}

void libhttppp::CtrlHandler::CTRLBreakEvent() {
    libhttppp::Event::_Restart = true;
    libhttppp::Event::_Run=false;
    libhttppp::Event::_Run=true;
}

void libhttppp::CtrlHandler::CTRLTermEvent() {
    libhttppp::Event::_Run = false;
}

void libhttppp::CtrlHandler::SIGPIPEEvent() {
    return;
}

void libhttppp::Event::runEventloop(){
        CpuInfo cpuinfo;
        size_t thrs = cpuinfo.getCores();
        initEventHandler();
MAINWORKERLOOP:
        ThreadPool thpool;
        for (size_t i = 0; i < thrs; i++) {
            Thread *cthread=thpool.addThread();
            cthread->Create(WorkerThread, (void*)this);
        }
        for (Thread *curth = thpool.getfirstThread(); curth; curth = curth->nextThread()) {
            curth->Join();
        }
        if(libhttppp::Event::_Restart){
            libhttppp::Event::_Restart=false;
            goto MAINWORKERLOOP;
        }
}

void * libhttppp::Event::WorkerThread(void* wrkevent){
    Event *eventptr=((Event*)wrkevent);
    int lstate=LockState::NOLOCK;
    while (libhttppp::Event::_Run) {
        try {
            for (int i = 0; i < eventptr->waitEventHandler(); ++i) {
                if((lstate=eventptr->LockConnection(i))!=LockState::ERRLOCK){
                    try{
                        switch(eventptr->StatusEventHandler(i)){
                            case EventApi::EventHandlerStatus::EVNOTREADY:
                                eventptr->ConnectEventHandler(i);
                                break;
                            case EventApi::EventHandlerStatus::EVIN:
                                eventptr->ReadEventHandler(i);
                                break;
                            case EventApi::EventHandlerStatus::EVOUT:
                                eventptr->WriteEventHandler(i);
                                break;
                            default:
                                eventptr->CloseEventHandler(i);
                                break;
                        }
                        eventptr->UnlockConnection(i);
                    }catch(HTTPException &e){
                        switch(e.getErrorType()){
                            case HTTPException::Critical:
                                throw e;
                                break;
                            case HTTPException::Error:
                                try{
                                    eventptr->CloseEventHandler(i);
                                }catch(HTTPException &e){
                                    eventptr->UnlockConnection(i);
                                    throw e;
                                }
                                break;
                        }
                    }
                }
            }
        }catch(HTTPException &e){
            switch(e.getErrorType()){
                case HTTPException::Critical:
                    throw e;
                    break;
                default:
                    Console con;
                    con << e.what() << con.endl;
            }
        }
    }
    return NULL;
}
<commit_msg>fixed deadlocks<commit_after>/*******************************************************************************
Copyright (c) 2019, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
	* Redistributions of source code must retain the above copyright
	  notice, this list of conditions and the following disclaimer.
	* Redistributions in binary form must reproduce the above copyright
	  notice, this list of conditions and the following disclaimer in the
	  documentation and/or other materials provided with the distribution.
	* Neither the name of the <organization> nor the
	  names of its contributors may be used to endorse or promote products
	  derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <iostream>

#include <config.h>

#include "connections.h"
#include "eventapi.h"
#include "threadpool.h"
#include "exception.h"

#include "event.h"

bool libhttppp::Event::_Run=true;
bool libhttppp::Event::_Restart=false;

libhttppp::Event::Event(libhttppp::ServerSocket* serversocket) : EVENT(serversocket){
    libhttppp::CtrlHandler::initCtrlHandler();
}

libhttppp::Event::~Event(){
}

libhttppp::EventApi::~EventApi(){
}

void libhttppp::CtrlHandler::CTRLCloseEvent() {
    libhttppp::Event::_Run = false;
}

void libhttppp::CtrlHandler::CTRLBreakEvent() {
    libhttppp::Event::_Restart = true;
    libhttppp::Event::_Run=false;
    libhttppp::Event::_Run=true;
}

void libhttppp::CtrlHandler::CTRLTermEvent() {
    libhttppp::Event::_Run = false;
}

void libhttppp::CtrlHandler::SIGPIPEEvent() {
    return;
}

void libhttppp::Event::runEventloop(){
        CpuInfo cpuinfo;
        size_t thrs = cpuinfo.getCores();
        initEventHandler();
MAINWORKERLOOP:
        ThreadPool thpool;
        for (size_t i = 0; i < thrs; i++) {
            Thread *cthread=thpool.addThread();
            cthread->Create(WorkerThread, (void*)this);
        }
        for (Thread *curth = thpool.getfirstThread(); curth; curth = curth->nextThread()) {
            curth->Join();
        }
        if(libhttppp::Event::_Restart){
            libhttppp::Event::_Restart=false;
            goto MAINWORKERLOOP;
        }
}

void * libhttppp::Event::WorkerThread(void* wrkevent){
    Event *eventptr=((Event*)wrkevent);
    int lstate=LockState::NOLOCK;
    while (libhttppp::Event::_Run) {
        try {
            for (int i = 0; i < eventptr->waitEventHandler(); ++i) {
                if((lstate=eventptr->LockConnection(i))!=LockState::ERRLOCK){
                    try{
                        switch(eventptr->StatusEventHandler(i)){
                            case EventApi::EventHandlerStatus::EVNOTREADY:
                                eventptr->ConnectEventHandler(i);
                                break;
                            case EventApi::EventHandlerStatus::EVIN:
                                eventptr->ReadEventHandler(i);
                                break;
                            case EventApi::EventHandlerStatus::EVOUT:
                                eventptr->WriteEventHandler(i);
                                break;
                            default:
                                eventptr->CloseEventHandler(i);
                                break;
                        }
                        eventptr->UnlockConnection(i);
                    }catch(HTTPException &e){
                        switch(e.getErrorType()){
                            case HTTPException::Critical:
                                throw e;
                                break;
                            case HTTPException::Error:
                                try{
                                    eventptr->CloseEventHandler(i);
                                }catch(HTTPException &e){
                                    if(e.getErrorType()==HTTPException::Critical){
                                        eventptr->UnlockConnection(i);
                                        throw e;
                                    }
                                }
                                break;
                        }
                        eventptr->UnlockConnection(i);
                    }
                }
            }
        }catch(HTTPException &e){
            switch(e.getErrorType()){
                case HTTPException::Critical:
                    throw e;
                    break;
                default:
                    Console con;
                    con << e.what() << con.endl;
            }
        }
    }
    return NULL;
}
<|endoftext|>


<commit_before>#include "aerial_autonomy/types/spiral_reference_trajectory.h"
#include <glog/logging.h>

constexpr double SpiralReferenceTrajectory::epsilon;
constexpr double SpiralReferenceTrajectory::gravity_magnitude_;

SpiralReferenceTrajectory::SpiralReferenceTrajectory(
    SpiralReferenceTrajectoryConfig config, ArmSineControllerConfig arm_config,
    Eigen::Vector3d current_position, double current_yaw)
    : ReferenceTrajectory(), config_(config), arm_config_(arm_config),
      current_position_(current_position), current_yaw_(current_yaw) {
  const auto &joint_config_vec = arm_config_.joint_config();
  CHECK(joint_config_vec.size() == 2) << "There should be two joints";
  CHECK(config.frequency_z() > 0)
      << "Z frequency cannot be 0 which implies z amplitude is infinity";
}

void SpiralReferenceTrajectory::getRP(
    double &roll, double &pitch, double yaw,
    Eigen::Vector3d acceleration_vector) const {
  Eigen::Vector3d unit_vec = acceleration_vector.normalized();
  double s_yaw = sin(yaw);
  double c_yaw = cos(yaw);
  double temp1 = unit_vec[0] * s_yaw - unit_vec[1] * c_yaw;
  if (std::abs(temp1) > 1.0) {
    LOG(WARNING) << "sin(roll) > 1";
    temp1 = std::copysign(1.0, temp1);
  }
  roll = std::asin(temp1);
  double temp2 = unit_vec[0] * c_yaw + unit_vec[1] * s_yaw;
  if (std::abs(temp2) < epsilon && std::abs(unit_vec[2]) < epsilon) {
    LOG(WARNING) << "Roll is +/-90degrees which is a singularity";
    pitch = 0;
  } else {
    double cos_inv = 1.0 / cos(roll);
    pitch = std::atan2(temp2 * cos_inv, unit_vec[2] * cos_inv);
  }
}

Eigen::Vector3d SpiralReferenceTrajectory::getAcceleration(double angle,
                                                           double omega_squared,
                                                           double rx,
                                                           double ry) const {
  // Counter gravity in addition to trajectory acceleration
  return Eigen::Vector3d(-rx * omega_squared * sin(angle),
                         -ry * omega_squared * cos(angle), gravity_magnitude_);
}

std::pair<Eigen::VectorXd, Eigen::VectorXd>
SpiralReferenceTrajectory::atTime(double t) const {
  // State: position, rpy, velocity, rpydot, rpyd, ja, jv, jad
  // Controls: thrust, rpyd_dot, jad_dot;
  Eigen::VectorXd x(21);
  Eigen::VectorXd u(6);
  const double rx = config_.radiusx();
  const double ry = config_.radiusy();
  const double omega = 2 * M_PI * config_.frequency();
  const double phase = config_.phase();
  const double velocity_z = config_.velocity_z();
  const double frequency_z = config_.frequency_z();
  const double frequency_yaw = config_.frequency_yaw();
  const double amplitude_yaw = config_.amplitude_yaw();
  double amplitude_z = velocity_z / frequency_z;
  double z_rem = std::fmod(t * frequency_z, 2.0);
  double sign = z_rem < 1.0 ? 1.0 : -1.0;
  double dist_z = z_rem < 1.0 ? z_rem : (2.0 - z_rem);
  double angle = omega * t + phase;
  double omega_yaw = 2 * M_PI * frequency_yaw;
  double angle_yaw = omega_yaw * t;
  double omega_squared = omega * omega;
  double signed_velocity_z = sign * velocity_z;
  double yaw = current_yaw_ + amplitude_yaw * sin(angle_yaw);
  // Position
  x[0] = current_position_[0] + rx * sin(angle);
  x[1] = current_position_[1] + ry * cos(angle);
  x[2] = current_position_[2] + amplitude_z * dist_z;
  // Rpy
  Eigen::Vector3d acceleration_vector =
      getAcceleration(angle, omega_squared, rx, ry);
  getRP(x[3], x[4], yaw, acceleration_vector);
  x[5] = yaw;
  // Velocities
  x[6] = rx * omega * cos(angle);
  x[7] = -ry * omega * sin(angle);
  x[8] = signed_velocity_z;
  // Finite difference to get rpydot
  double dt = 1e-6;
  double angle_delta = angle + omega * dt;
  Eigen::Vector3d acceleration_vector_dt =
      getAcceleration(angle_delta, omega_squared, rx, ry);
  double yaw_dt = yaw + amplitude_yaw * sin(angle_yaw + omega_yaw * dt);
  double roll_dt, pitch_dt;
  getRP(roll_dt, pitch_dt, yaw_dt, acceleration_vector_dt);
  x[9] = (roll_dt - x[3]) / dt;
  x[10] = (pitch_dt - x[4]) / dt;
  x[11] = amplitude_yaw * omega_yaw * cos(angle_yaw);
  // Commanded angles
  x[12] = x[3];
  x[13] = x[4];
  x[14] = x[5];
  // Joint state
  const auto &joint_config_vec = arm_config_.joint_config();
  int start = 15;
  // Assuming 2 joints
  for (int i = 0; i < 2; ++i) {
    const auto &joint_config = joint_config_vec.Get(i);
    const double &amp = joint_config.amplitude();
    const double &phi = joint_config.phase();
    const double &offset = joint_config.offset();
    double omega = 2 * M_PI * joint_config.frequency();
    double angle = omega * t + phi;
    x[start + i] = offset + amp * sin(angle);
    x[start + i + 2] = amp * omega * cos(angle);
    x[start + i + 4] = x[start + i];
  }
  // Controls
  u[0] = acceleration_vector.norm() / gravity_magnitude_;
  // rpyd_dot = rpy_dot
  u[1] = x[9];
  u[2] = x[10];
  u[3] = x[11];
  // jad_dot = ja_dot
  u[4] = x[17];
  u[5] = x[18];
  return std::make_pair(x, u);
}
<commit_msg>Start spiral from the current position<commit_after>#include "aerial_autonomy/types/spiral_reference_trajectory.h"
#include <glog/logging.h>

constexpr double SpiralReferenceTrajectory::epsilon;
constexpr double SpiralReferenceTrajectory::gravity_magnitude_;

SpiralReferenceTrajectory::SpiralReferenceTrajectory(
    SpiralReferenceTrajectoryConfig config, ArmSineControllerConfig arm_config,
    Eigen::Vector3d current_position, double current_yaw)
    : ReferenceTrajectory(), config_(config), arm_config_(arm_config),
      current_position_(current_position), current_yaw_(current_yaw) {
  const auto &joint_config_vec = arm_config_.joint_config();
  CHECK(joint_config_vec.size() == 2) << "There should be two joints";
  CHECK(config.frequency_z() > 0)
      << "Z frequency cannot be 0 which implies z amplitude is infinity";
}

void SpiralReferenceTrajectory::getRP(
    double &roll, double &pitch, double yaw,
    Eigen::Vector3d acceleration_vector) const {
  Eigen::Vector3d unit_vec = acceleration_vector.normalized();
  double s_yaw = sin(yaw);
  double c_yaw = cos(yaw);
  double temp1 = unit_vec[0] * s_yaw - unit_vec[1] * c_yaw;
  if (std::abs(temp1) > 1.0) {
    LOG(WARNING) << "sin(roll) > 1";
    temp1 = std::copysign(1.0, temp1);
  }
  roll = std::asin(temp1);
  double temp2 = unit_vec[0] * c_yaw + unit_vec[1] * s_yaw;
  if (std::abs(temp2) < epsilon && std::abs(unit_vec[2]) < epsilon) {
    LOG(WARNING) << "Roll is +/-90degrees which is a singularity";
    pitch = 0;
  } else {
    double cos_inv = 1.0 / cos(roll);
    pitch = std::atan2(temp2 * cos_inv, unit_vec[2] * cos_inv);
  }
}

Eigen::Vector3d SpiralReferenceTrajectory::getAcceleration(double angle,
                                                           double omega_squared,
                                                           double rx,
                                                           double ry) const {
  // Counter gravity in addition to trajectory acceleration
  return Eigen::Vector3d(-rx * omega_squared * sin(angle),
                         -ry * omega_squared * cos(angle), gravity_magnitude_);
}

std::pair<Eigen::VectorXd, Eigen::VectorXd>
SpiralReferenceTrajectory::atTime(double t) const {
  // State: position, rpy, velocity, rpydot, rpyd, ja, jv, jad
  // Controls: thrust, rpyd_dot, jad_dot;
  Eigen::VectorXd x(21);
  Eigen::VectorXd u(6);
  const double rx = config_.radiusx();
  const double ry = config_.radiusy();
  const double omega = 2 * M_PI * config_.frequency();
  const double phase = config_.phase();
  const double velocity_z = config_.velocity_z();
  const double frequency_z = config_.frequency_z();
  const double frequency_yaw = config_.frequency_yaw();
  const double amplitude_yaw = config_.amplitude_yaw();
  double amplitude_z = velocity_z / frequency_z;
  double z_rem = std::fmod(t * frequency_z, 2.0);
  double sign = z_rem < 1.0 ? 1.0 : -1.0;
  double dist_z = z_rem < 1.0 ? z_rem : (2.0 - z_rem);
  double angle = omega * t + phase;
  double omega_yaw = 2 * M_PI * frequency_yaw;
  double angle_yaw = omega_yaw * t;
  double omega_squared = omega * omega;
  double signed_velocity_z = sign * velocity_z;
  double yaw = current_yaw_ + amplitude_yaw * sin(angle_yaw);
  // Position
  x[0] = current_position_[0] - rx * sin(phase) + rx * sin(angle);
  x[1] = current_position_[1] - ry * cos(phase) + ry * cos(angle);
  x[2] = current_position_[2] + amplitude_z * dist_z;
  // Rpy
  Eigen::Vector3d acceleration_vector =
      getAcceleration(angle, omega_squared, rx, ry);
  getRP(x[3], x[4], yaw, acceleration_vector);
  x[5] = yaw;
  // Velocities
  x[6] = rx * omega * cos(angle);
  x[7] = -ry * omega * sin(angle);
  x[8] = signed_velocity_z;
  // Finite difference to get rpydot
  double dt = 1e-6;
  double angle_delta = angle + omega * dt;
  Eigen::Vector3d acceleration_vector_dt =
      getAcceleration(angle_delta, omega_squared, rx, ry);
  double yaw_dt = yaw + amplitude_yaw * sin(angle_yaw + omega_yaw * dt);
  double roll_dt, pitch_dt;
  getRP(roll_dt, pitch_dt, yaw_dt, acceleration_vector_dt);
  x[9] = (roll_dt - x[3]) / dt;
  x[10] = (pitch_dt - x[4]) / dt;
  x[11] = amplitude_yaw * omega_yaw * cos(angle_yaw);
  // Commanded angles
  x[12] = x[3];
  x[13] = x[4];
  x[14] = x[5];
  // Joint state
  const auto &joint_config_vec = arm_config_.joint_config();
  int start = 15;
  // Assuming 2 joints
  for (int i = 0; i < 2; ++i) {
    const auto &joint_config = joint_config_vec.Get(i);
    const double &amp = joint_config.amplitude();
    const double &phi = joint_config.phase();
    const double &offset = joint_config.offset();
    double omega = 2 * M_PI * joint_config.frequency();
    double angle = omega * t + phi;
    x[start + i] = offset + amp * sin(angle);
    x[start + i + 2] = amp * omega * cos(angle);
    x[start + i + 4] = x[start + i];
  }
  // Controls
  u[0] = acceleration_vector.norm() / gravity_magnitude_;
  // rpyd_dot = rpy_dot
  u[1] = x[9];
  u[2] = x[10];
  u[3] = x[11];
  // jad_dot = ja_dot
  u[4] = x[17];
  u[5] = x[18];
  return std::make_pair(x, u);
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "tsdb" project
 *   Copyright (c) 2015 Paul Asmuth, FnordCorp B.V.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <zbase/core/LogPartitionReplication.h>
#include <zbase/core/LogPartitionReader.h>
#include <zbase/core/ReplicationScheme.h>
#include <stx/logging.h>
#include <stx/io/fileutil.h>
#include <stx/protobuf/msg.h>

using namespace stx;

namespace zbase {

const size_t LogPartitionReplication::kMaxBatchSizeRows = 8192;
const size_t LogPartitionReplication::kMaxBatchSizeBytes = 1024 * 1024 * 50; // 50 MB

LogPartitionReplication::LogPartitionReplication(
    RefPtr<Partition> partition,
    RefPtr<ReplicationScheme> repl_scheme,
    http::HTTPConnectionPool* http) :
    PartitionReplication(partition, repl_scheme, http) {}

bool LogPartitionReplication::needsReplication() const {
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  if (replicas.size() == 0) {
    return false;
  }

  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;
  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);
    if (replica_offset < head_offset) {
      return true;
    }
  }

  return false;
}

size_t LogPartitionReplication::numFullRemoteCopies() const {
  size_t ncopies = 0;
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;

  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);
    if (replica_offset >= head_offset) {
      ncopies += 1;
    }
  }

  return ncopies;
}

void LogPartitionReplication::replicateTo(
    const ReplicaRef& replica,
    uint64_t replicated_offset) {
  if (replica.is_local) {
    RAISE(kIllegalStateError, "can't replicate to myself");
  }

  LogPartitionReader reader(partition_->getTable(), snap_);

  size_t batch_size = 0;
  RecordEnvelopeList batch;
  reader.fetchRecords(
      replicated_offset,
      size_t(-1),
      [this, &batch, &replica, &replicated_offset, &batch_size] (
          const SHA1Hash& record_id,
          const void* record_data,
          size_t record_size) {
    auto rec = batch.add_records();
    rec->set_tsdb_namespace(snap_->state.tsdb_namespace());
    rec->set_table_name(snap_->state.table_key());
    rec->set_partition_sha1(snap_->key.toString());
    rec->set_record_id(record_id.toString());
    rec->set_record_data(record_data, record_size);

    batch_size += record_size;

    if (batch_size > kMaxBatchSizeBytes ||
        batch.records().size() > kMaxBatchSizeRows) {
      uploadBatchTo(replica, batch);
      batch.mutable_records()->Clear();
      batch_size = 0;
    }
  });

  if (batch.records().size() > 0) {
    uploadBatchTo(replica, batch);
  }
}

void LogPartitionReplication::uploadBatchTo(
    const ReplicaRef& replica,
    const RecordEnvelopeList& batch) {
  auto body = msg::encode(batch);
  URI uri(StringUtil::format("http://$0/tsdb/insert", replica.addr.hostAndPort()));
  http::HTTPRequest req(http::HTTPMessage::M_POST, uri.pathAndQuery());
  req.addHeader("Host", uri.hostAndPort());
  req.addHeader("Content-Type", "application/fnord-msg");
  req.addBody(body->data(), body->size());

  auto res = http_->executeRequest(req);
  res.wait();

  const auto& r = res.get();
  if (r.statusCode() != 201) {
    RAISEF(kRuntimeError, "received non-201 response: $0", r.body().toString());
  }
}

bool LogPartitionReplication::replicate() {
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  if (replicas.size() == 0) {
    return true;
  }

  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;
  bool dirty = false;
  bool success = true;

  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);

    if (replica_offset < head_offset) {
      logTrace(
          "tsdb",
          "Replicating partition $0/$1/$2 to $3",
          snap_->state.tsdb_namespace(),
          snap_->state.table_key(),
          snap_->key.toString(),
          r.addr.hostAndPort());

      try {
        replicateTo(r, replica_offset);
        setReplicatedOffsetFor(&repl_state, r.unique_id, head_offset);
        dirty = true;
      } catch (const std::exception& e) {
        success = false;

        stx::logError(
          "tsdb",
          e,
          "Error while replicating partition $0/$1/$2 to $3",
          snap_->state.tsdb_namespace(),
          snap_->state.table_key(),
          snap_->key.toString(),
          r.addr.hostAndPort());
      }
    }
  }

  if (dirty) {
    commitReplicationState(repl_state);
  }

  return success;
}

} // namespace tdsb

<commit_msg>add assert in LogPartitionReplication<commit_after>/**
 * This file is part of the "tsdb" project
 *   Copyright (c) 2015 Paul Asmuth, FnordCorp B.V.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <zbase/core/LogPartitionReplication.h>
#include <zbase/core/LogPartitionReader.h>
#include <zbase/core/ReplicationScheme.h>
#include <stx/logging.h>
#include <stx/io/fileutil.h>
#include <stx/protobuf/msg.h>

using namespace stx;

namespace zbase {

const size_t LogPartitionReplication::kMaxBatchSizeRows = 8192;
const size_t LogPartitionReplication::kMaxBatchSizeBytes = 1024 * 1024 * 50; // 50 MB

LogPartitionReplication::LogPartitionReplication(
    RefPtr<Partition> partition,
    RefPtr<ReplicationScheme> repl_scheme,
    http::HTTPConnectionPool* http) :
    PartitionReplication(partition, repl_scheme, http) {}

bool LogPartitionReplication::needsReplication() const {
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  if (replicas.size() == 0) {
    return false;
  }

  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;
  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);
    if (replica_offset < head_offset) {
      return true;
    }
  }

  return false;
}

size_t LogPartitionReplication::numFullRemoteCopies() const {
  size_t ncopies = 0;
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;

  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);
    if (replica_offset >= head_offset) {
      ncopies += 1;
    }
  }

  return ncopies;
}

void LogPartitionReplication::replicateTo(
    const ReplicaRef& replica,
    uint64_t replicated_offset) {
  if (replica.is_local) {
    RAISE(kIllegalStateError, "can't replicate to myself");
  }

  LogPartitionReader reader(partition_->getTable(), snap_);

  size_t batch_size = 0;
  size_t num_replicated = 0;
  RecordEnvelopeList batch;
  reader.fetchRecords(
      replicated_offset,
      size_t(-1),
      [this, &batch, &replica, &replicated_offset, &batch_size, &num_replicated] (
          const SHA1Hash& record_id,
          const void* record_data,
          size_t record_size) {
    auto rec = batch.add_records();
    rec->set_tsdb_namespace(snap_->state.tsdb_namespace());
    rec->set_table_name(snap_->state.table_key());
    rec->set_partition_sha1(snap_->key.toString());
    rec->set_record_id(record_id.toString());
    rec->set_record_data(record_data, record_size);

    batch_size += record_size;
    ++num_replicated;

    if (batch_size > kMaxBatchSizeBytes ||
        batch.records().size() > kMaxBatchSizeRows) {
      uploadBatchTo(replica, batch);
      batch.mutable_records()->Clear();
      batch_size = 0;
    }
  });

  if (batch.records().size() > 0) {
    uploadBatchTo(replica, batch);
  }

  if (num_replicated != snap_->nrecs - replicated_offset) {
    RAISEF(
        kIllegalStateError,
        "expected to replicate $0 records, but only saw $1",
        snap_->nrecs - replicated_offset,
        num_replicated);
  }
}

void LogPartitionReplication::uploadBatchTo(
    const ReplicaRef& replica,
    const RecordEnvelopeList& batch) {
  auto body = msg::encode(batch);
  URI uri(StringUtil::format("http://$0/tsdb/insert", replica.addr.hostAndPort()));
  http::HTTPRequest req(http::HTTPMessage::M_POST, uri.pathAndQuery());
  req.addHeader("Host", uri.hostAndPort());
  req.addHeader("Content-Type", "application/fnord-msg");
  req.addBody(body->data(), body->size());

  auto res = http_->executeRequest(req);
  res.wait();

  const auto& r = res.get();
  if (r.statusCode() != 201) {
    RAISEF(kRuntimeError, "received non-201 response: $0", r.body().toString());
  }
}

bool LogPartitionReplication::replicate() {
  auto replicas = repl_scheme_->replicasFor(snap_->key);
  if (replicas.size() == 0) {
    return true;
  }

  auto repl_state = fetchReplicationState();
  auto head_offset = snap_->nrecs;
  bool dirty = false;
  bool success = true;

  for (const auto& r : replicas) {
    if (r.is_local) {
      continue;
    }

    const auto& replica_offset = replicatedOffsetFor(repl_state, r.unique_id);

    if (replica_offset < head_offset) {
      logTrace(
          "tsdb",
          "Replicating partition $0/$1/$2 to $3",
          snap_->state.tsdb_namespace(),
          snap_->state.table_key(),
          snap_->key.toString(),
          r.addr.hostAndPort());

      try {
        replicateTo(r, replica_offset);
        setReplicatedOffsetFor(&repl_state, r.unique_id, head_offset);
        dirty = true;
      } catch (const std::exception& e) {
        success = false;

        stx::logError(
          "tsdb",
          e,
          "Error while replicating partition $0/$1/$2 to $3",
          snap_->state.tsdb_namespace(),
          snap_->state.table_key(),
          snap_->key.toString(),
          r.addr.hostAndPort());
      }
    }
  }

  if (dirty) {
    commitReplicationState(repl_state);
  }

  return success;
}

} // namespace tdsb

<|endoftext|>