<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include <gmock/gmock.h>

#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include <process/collect.hpp>
#include <process/future.hpp>
#include <process/gmock.hpp>
#include <process/gtest.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <stout/duration.hpp>
#include <stout/gtest.hpp>
#include <stout/hashset.hpp>
#include <stout/stopwatch.hpp>

namespace http = process::http;

using process::Future;
using process::Owned;
using process::Process;
using process::ProcessBase;
using process::Promise;
using process::UPID;

using std::cout;
using std::endl;
using std::list;
using std::ostringstream;
using std::string;
using std::vector;

int main(int argc, char** argv)
{
  // Initialize Google Mock/Test.
  testing::InitGoogleMock(&argc, argv);

  // Add the libprocess test event listeners.
  ::testing::TestEventListeners& listeners =
    ::testing::UnitTest::GetInstance()->listeners();

  listeners.Append(process::ClockTestEventListener::instance());
  listeners.Append(process::FilterTestEventListener::instance());

  return RUN_ALL_TESTS();
}

// TODO(jmlvanre): Factor out the client / server behavior so that we
// can make separate binaries for the client and server. This is
// useful to attach performance tools to them separately.

// A process that emulates the 'client' side of a ping pong game.
// An HTTP '/run' request performs a run and returns the time elapsed.
class ClientProcess : public Process<ClientProcess>
{
public:
  ClientProcess()
    : requests(0),
      responses(0),
      totalRequests(0),
      concurrency(0) {}

  virtual ~ClientProcess() {}

protected:
  virtual void initialize()
  {
    install("pong", &ClientProcess::pong);

    route("/run", None(), &ClientProcess::run);
  }

private:
  Future<http::Response> run(const http::Request& request)
  {
    if (duration.get() != nullptr) {
      return http::BadRequest("A run is already in progress");
    }

    hashmap<string, Option<string>> parameters {
      {"server", request.url.query.get("server")},
      {"messageSize", request.url.query.get("messageSize")},
      {"requests", request.url.query.get("requests")},
      {"concurrency", request.url.query.get("concurrency")},
    };

    // Ensure all parameters were provided.
    foreachpair (const string& parameter,
                 const Option<string>& value,
                 parameters) {
      if (value.isNone()) {
        return http::BadRequest("Missing '" + parameter + "' parameter");
      }
    }

    server = UPID(parameters["server"].get());
    link(server);

    Try<Bytes> messageSize = Bytes::parse(parameters["messageSize"].get());
    if (messageSize.isError()) {
      return http::BadRequest("Invalid 'messageSize': " + messageSize.error());
    }
    message = string(messageSize.get().bytes(), '1');

    Try<size_t> numify_ = numify<size_t>(parameters["requests"].get());
    if (numify_.isError()) {
      return http::BadRequest("Invalid 'requests': " + numify_.error());
    }
    totalRequests = numify_.get();

    numify_ = numify<size_t>(parameters["concurrency"].get());
    if (numify_.isError()) {
      return http::BadRequest("Invalid 'concurrency': " + numify_.error());
    }
    concurrency = numify_.get();

    if (concurrency > totalRequests) {
      concurrency = totalRequests;
    }

    return _run()
      .then([](const Duration& duration) -> Future<http::Response> {
        return http::OK(stringify(duration));
      });
  }

  Future<Duration> _run()
  {
    duration = Owned<Promise<Duration>>(new Promise<Duration>());

    watch.start();

    while (requests < concurrency) {
      send(server, "ping", message.c_str(), message.size());
      ++requests;
    }

    return duration->future();
  }

  void pong(const UPID& from, const string& body)
  {
    ++responses;

    if (responses == totalRequests) {
      duration->set(watch.elapsed());
      duration.reset();
    } else if (requests < totalRequests) {
      send(server, "ping", message.c_str(), message.size());
      ++requests;
    }
  }

  // The address of the ponger (server).
  UPID server;

  Stopwatch watch;

  Owned<Promise<Duration>> duration;

  string message;

  size_t requests;
  size_t responses;

  size_t totalRequests;
  size_t concurrency;
};

// A process that emulates the 'server' side of a ping pong game.
// Note that the server links to any clients communicating to it.
class ServerProcess : public Process<ServerProcess>
{
public:
  virtual ~ServerProcess() {}

protected:
  virtual void initialize()
  {
    install("ping", &ServerProcess::ping);
  }

private:
  void ping(const UPID& from, const string& body)
  {
    if (!links.contains(from)) {
      link(from);
      links.insert(from);
    }

    send(from, "pong", body.c_str(), body.size());
  }

  hashset<UPID> links;
};

// TODO(bmahler): Since there is no forking here, libprocess
// avoids going through sockets for local messages. Either fork
// or have the ability to disable local messages in libprocess.

// Launches many clients against a central server and measures
// client throughput.
TEST(ProcessTest, Process_BENCHMARK_ClientServer)
{
  const size_t numRequests = 10000;
  const size_t concurrency = 250;
  const size_t numClients = 8;
  const Bytes messageSize = Bytes(3);

  ServerProcess server;
  const UPID serverPid = spawn(&server);

  // Launch the clients.
  vector<Owned<ClientProcess>> clients;
  for (size_t i = 0; i < numClients; i++) {
    clients.push_back(Owned<ClientProcess>(new ClientProcess()));
    spawn(clients.back().get());
  }

  // Start the ping / pongs!
  const string query = strings::join(
      "&",
      "server=" + stringify(serverPid),
      "requests=" + stringify(numRequests),
      "concurrency=" + stringify(concurrency),
      "messageSize=" + stringify(messageSize));

  Stopwatch watch;
  watch.start();

  list<Future<http::Response>> futures;
  foreach (const Owned<ClientProcess>& client, clients) {
    futures.push_back(http::get(client->self(), "run", query));
  }

  Future<list<http::Response>> responses = collect(futures);
  AWAIT_READY(responses);

  Duration elapsed = watch.elapsed();

  // Print the throughput of each client.
  size_t i = 0;
  foreach (const http::Response& response, responses.get()) {
    ASSERT_EQ(http::Status::OK, response.code);
    ASSERT_EQ(http::Status::string(http::Status::OK), response.status);

    Try<Duration> elapsed = Duration::parse(response.body);
    ASSERT_SOME(elapsed);
    double throughput = numRequests / elapsed.get().secs();

    cout << "Client " << i << ": " << throughput << " rpcs / sec" << endl;

    i++;
  }

  double throughput = (numRequests * numClients) / elapsed.secs();
  cout << "Estimated Total: " << throughput << " rpcs / sec" << endl;

  foreach (const Owned<ClientProcess>& client, clients) {
    terminate(*client);
    wait(*client);
  }

  terminate(server);
  wait(server);

  return;
}

class LinkerProcess : public Process<LinkerProcess>
{
public:
  LinkerProcess(const UPID& _to) : to(_to) {}

  virtual void initialize()
  {
    link(to);
  }

private:
  UPID to;
};

class EphemeralProcess : public Process<EphemeralProcess>
{
public:
  void terminate()
  {
    process::terminate(self());
  }
};

// Simulate the scenario discussed in MESOS-2182. We first establish a
// large number of links by creating many linker-linkee pairs. And
// then, we introduce a large amount of ephemeral process exits as
// well as event dispatches.
TEST(ProcessTest, Process_BENCHMARK_LargeNumberOfLinks)
{
  int links = 5000;
  int iterations = 10000;

  // Keep track of all the linked processes we created.
  vector<ProcessBase*> processes;

  // Establish a large number of links.
  for (int i = 0; i < links; i++) {
    ProcessBase* linkee = new ProcessBase();
    LinkerProcess* linker = new LinkerProcess(linkee->self());

    processes.push_back(linkee);
    processes.push_back(linker);

    spawn(linkee);
    spawn(linker);
  }

  // Generate large number of dispatches and process exits by spawning
  // and then terminating EphemeralProcesses.
  vector<ProcessBase*> ephemeralProcesses;

  Stopwatch watch;
  watch.start();

  for (int i = 0; i < iterations ; i++) {
    EphemeralProcess* process = new EphemeralProcess();
    ephemeralProcesses.push_back(process);

    spawn(process);

    // NOTE: We let EphemeralProcess terminate itself to make sure all
    // dispatches are actually executed (otherwise, 'wait' below will
    // be blocked).
    dispatch(process->self(), &EphemeralProcess::terminate);
  }

  foreach (ProcessBase* process, ephemeralProcesses) {
    wait(process);
    delete process;
  }

  cout << "Elapsed: " << watch.elapsed() << endl;

  foreach (ProcessBase* process, processes) {
    terminate(process);
    wait(process);
    delete process;
  }
}
<commit_msg>Removed a redundant `return` statement in libprocess.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <gtest/gtest.h>

#include <gmock/gmock.h>

#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include <process/collect.hpp>
#include <process/future.hpp>
#include <process/gmock.hpp>
#include <process/gtest.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>

#include <stout/duration.hpp>
#include <stout/gtest.hpp>
#include <stout/hashset.hpp>
#include <stout/stopwatch.hpp>

namespace http = process::http;

using process::Future;
using process::Owned;
using process::Process;
using process::ProcessBase;
using process::Promise;
using process::UPID;

using std::cout;
using std::endl;
using std::list;
using std::ostringstream;
using std::string;
using std::vector;

int main(int argc, char** argv)
{
  // Initialize Google Mock/Test.
  testing::InitGoogleMock(&argc, argv);

  // Add the libprocess test event listeners.
  ::testing::TestEventListeners& listeners =
    ::testing::UnitTest::GetInstance()->listeners();

  listeners.Append(process::ClockTestEventListener::instance());
  listeners.Append(process::FilterTestEventListener::instance());

  return RUN_ALL_TESTS();
}

// TODO(jmlvanre): Factor out the client / server behavior so that we
// can make separate binaries for the client and server. This is
// useful to attach performance tools to them separately.

// A process that emulates the 'client' side of a ping pong game.
// An HTTP '/run' request performs a run and returns the time elapsed.
class ClientProcess : public Process<ClientProcess>
{
public:
  ClientProcess()
    : requests(0),
      responses(0),
      totalRequests(0),
      concurrency(0) {}

  virtual ~ClientProcess() {}

protected:
  virtual void initialize()
  {
    install("pong", &ClientProcess::pong);

    route("/run", None(), &ClientProcess::run);
  }

private:
  Future<http::Response> run(const http::Request& request)
  {
    if (duration.get() != nullptr) {
      return http::BadRequest("A run is already in progress");
    }

    hashmap<string, Option<string>> parameters {
      {"server", request.url.query.get("server")},
      {"messageSize", request.url.query.get("messageSize")},
      {"requests", request.url.query.get("requests")},
      {"concurrency", request.url.query.get("concurrency")},
    };

    // Ensure all parameters were provided.
    foreachpair (const string& parameter,
                 const Option<string>& value,
                 parameters) {
      if (value.isNone()) {
        return http::BadRequest("Missing '" + parameter + "' parameter");
      }
    }

    server = UPID(parameters["server"].get());
    link(server);

    Try<Bytes> messageSize = Bytes::parse(parameters["messageSize"].get());
    if (messageSize.isError()) {
      return http::BadRequest("Invalid 'messageSize': " + messageSize.error());
    }
    message = string(messageSize.get().bytes(), '1');

    Try<size_t> numify_ = numify<size_t>(parameters["requests"].get());
    if (numify_.isError()) {
      return http::BadRequest("Invalid 'requests': " + numify_.error());
    }
    totalRequests = numify_.get();

    numify_ = numify<size_t>(parameters["concurrency"].get());
    if (numify_.isError()) {
      return http::BadRequest("Invalid 'concurrency': " + numify_.error());
    }
    concurrency = numify_.get();

    if (concurrency > totalRequests) {
      concurrency = totalRequests;
    }

    return _run()
      .then([](const Duration& duration) -> Future<http::Response> {
        return http::OK(stringify(duration));
      });
  }

  Future<Duration> _run()
  {
    duration = Owned<Promise<Duration>>(new Promise<Duration>());

    watch.start();

    while (requests < concurrency) {
      send(server, "ping", message.c_str(), message.size());
      ++requests;
    }

    return duration->future();
  }

  void pong(const UPID& from, const string& body)
  {
    ++responses;

    if (responses == totalRequests) {
      duration->set(watch.elapsed());
      duration.reset();
    } else if (requests < totalRequests) {
      send(server, "ping", message.c_str(), message.size());
      ++requests;
    }
  }

  // The address of the ponger (server).
  UPID server;

  Stopwatch watch;

  Owned<Promise<Duration>> duration;

  string message;

  size_t requests;
  size_t responses;

  size_t totalRequests;
  size_t concurrency;
};

// A process that emulates the 'server' side of a ping pong game.
// Note that the server links to any clients communicating to it.
class ServerProcess : public Process<ServerProcess>
{
public:
  virtual ~ServerProcess() {}

protected:
  virtual void initialize()
  {
    install("ping", &ServerProcess::ping);
  }

private:
  void ping(const UPID& from, const string& body)
  {
    if (!links.contains(from)) {
      link(from);
      links.insert(from);
    }

    send(from, "pong", body.c_str(), body.size());
  }

  hashset<UPID> links;
};

// TODO(bmahler): Since there is no forking here, libprocess
// avoids going through sockets for local messages. Either fork
// or have the ability to disable local messages in libprocess.

// Launches many clients against a central server and measures
// client throughput.
TEST(ProcessTest, Process_BENCHMARK_ClientServer)
{
  const size_t numRequests = 10000;
  const size_t concurrency = 250;
  const size_t numClients = 8;
  const Bytes messageSize = Bytes(3);

  ServerProcess server;
  const UPID serverPid = spawn(&server);

  // Launch the clients.
  vector<Owned<ClientProcess>> clients;
  for (size_t i = 0; i < numClients; i++) {
    clients.push_back(Owned<ClientProcess>(new ClientProcess()));
    spawn(clients.back().get());
  }

  // Start the ping / pongs!
  const string query = strings::join(
      "&",
      "server=" + stringify(serverPid),
      "requests=" + stringify(numRequests),
      "concurrency=" + stringify(concurrency),
      "messageSize=" + stringify(messageSize));

  Stopwatch watch;
  watch.start();

  list<Future<http::Response>> futures;
  foreach (const Owned<ClientProcess>& client, clients) {
    futures.push_back(http::get(client->self(), "run", query));
  }

  Future<list<http::Response>> responses = collect(futures);
  AWAIT_READY(responses);

  Duration elapsed = watch.elapsed();

  // Print the throughput of each client.
  size_t i = 0;
  foreach (const http::Response& response, responses.get()) {
    ASSERT_EQ(http::Status::OK, response.code);
    ASSERT_EQ(http::Status::string(http::Status::OK), response.status);

    Try<Duration> elapsed = Duration::parse(response.body);
    ASSERT_SOME(elapsed);
    double throughput = numRequests / elapsed.get().secs();

    cout << "Client " << i << ": " << throughput << " rpcs / sec" << endl;

    i++;
  }

  double throughput = (numRequests * numClients) / elapsed.secs();
  cout << "Estimated Total: " << throughput << " rpcs / sec" << endl;

  foreach (const Owned<ClientProcess>& client, clients) {
    terminate(*client);
    wait(*client);
  }

  terminate(server);
  wait(server);
}

class LinkerProcess : public Process<LinkerProcess>
{
public:
  LinkerProcess(const UPID& _to) : to(_to) {}

  virtual void initialize()
  {
    link(to);
  }

private:
  UPID to;
};

class EphemeralProcess : public Process<EphemeralProcess>
{
public:
  void terminate()
  {
    process::terminate(self());
  }
};

// Simulate the scenario discussed in MESOS-2182. We first establish a
// large number of links by creating many linker-linkee pairs. And
// then, we introduce a large amount of ephemeral process exits as
// well as event dispatches.
TEST(ProcessTest, Process_BENCHMARK_LargeNumberOfLinks)
{
  int links = 5000;
  int iterations = 10000;

  // Keep track of all the linked processes we created.
  vector<ProcessBase*> processes;

  // Establish a large number of links.
  for (int i = 0; i < links; i++) {
    ProcessBase* linkee = new ProcessBase();
    LinkerProcess* linker = new LinkerProcess(linkee->self());

    processes.push_back(linkee);
    processes.push_back(linker);

    spawn(linkee);
    spawn(linker);
  }

  // Generate large number of dispatches and process exits by spawning
  // and then terminating EphemeralProcesses.
  vector<ProcessBase*> ephemeralProcesses;

  Stopwatch watch;
  watch.start();

  for (int i = 0; i < iterations ; i++) {
    EphemeralProcess* process = new EphemeralProcess();
    ephemeralProcesses.push_back(process);

    spawn(process);

    // NOTE: We let EphemeralProcess terminate itself to make sure all
    // dispatches are actually executed (otherwise, 'wait' below will
    // be blocked).
    dispatch(process->self(), &EphemeralProcess::terminate);
  }

  foreach (ProcessBase* process, ephemeralProcesses) {
    wait(process);
    delete process;
  }

  cout << "Elapsed: " << watch.elapsed() << endl;

  foreach (ProcessBase* process, processes) {
    terminate(process);
    wait(process);
    delete process;
  }
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Colin Girling

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 "../Test.hpp"
#include "../TestConverterUtility.hpp"
#include "../TestMemoryUtility.hpp"

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, bool_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef bool type;

    TEST_OVERRIDE_ARGS("bool,size_type");

    type value = true;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "true");
    CHECK_EQUAL(length, 4U);
    TestMemoryUtility<char>::SafeFree(str);

    value = false;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "false");
    CHECK_EQUAL(length, 5U);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef char type;

    TEST_OVERRIDE_ARGS("char,size_type");

    type value = '1';
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "1");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef signed char type;

    TEST_OVERRIDE_ARGS("signed char,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef unsigned char type;

    TEST_OVERRIDE_ARGS("unsigned char,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_short_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef signed short type;

    TEST_OVERRIDE_ARGS("signed short,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_short_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef unsigned short type;

    TEST_OVERRIDE_ARGS("unsigned short,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_int_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef signed int type;

    TEST_OVERRIDE_ARGS("signed int,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_int_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef unsigned int type;

    TEST_OVERRIDE_ARGS("unsigned int,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_long_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef signed long type;

    TEST_OVERRIDE_ARGS("signed long,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_long_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef unsigned long type;

    TEST_OVERRIDE_ARGS("unsigned long,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);
}
<commit_msg>Added unit tests for min/max ranges for all integer types for TestConverterUtility class.<commit_after>/*
Copyright 2016 Colin Girling

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 "../Test.hpp"
#include "../TestConverterUtility.hpp"
#include "../TestMemoryUtility.hpp"
#include "../TestStringUtility.hpp"
#include "../TestMinMax.hpp"

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, bool_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef bool type;

    TEST_OVERRIDE_ARGS("bool,size_type");

    type value = true;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "true");
    CHECK_EQUAL(length, 4U);
    TestMemoryUtility<char>::SafeFree(str);

    value = false;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "false");
    CHECK_EQUAL(length, 5U);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    typedef unsigned int size_type;
    typedef char type;

    TEST_OVERRIDE_ARGS("char,size_type");

    type value = '1';
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "1");
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef signed char type;

    TEST_OVERRIDE_ARGS("signed char,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* min_str = TestStringUtility::GetMinSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::min_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, min_str);
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_char_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef unsigned char type;

    TEST_OVERRIDE_ARGS("unsigned char,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxUnsignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_short_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef signed short type;

    TEST_OVERRIDE_ARGS("signed short,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* min_str = TestStringUtility::GetMinSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::min_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, min_str);
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_short_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef unsigned short type;

    TEST_OVERRIDE_ARGS("unsigned short,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxUnsignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_int_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef signed int type;

    TEST_OVERRIDE_ARGS("signed int,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* min_str = TestStringUtility::GetMinSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::min_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, min_str);
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_int_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef unsigned int type;

    TEST_OVERRIDE_ARGS("unsigned int,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxUnsignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, signed_long_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef signed long type;

    TEST_OVERRIDE_ARGS("signed long,size_type");

    type value = 0;
    size_type length = 0;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* min_str = TestStringUtility::GetMinSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::min_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, min_str);
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxSignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}

TEST_MEMBER_FUNCTION(TestConverterUtility, GetString, unsigned_long_size_type)
{
    using ocl::TestConverterUtility;
    using ocl::TestMemoryUtility;
    using ocl::TestStringUtility;
    using ocl::TestMinMax;
    typedef unsigned int size_type;
    typedef unsigned long type;

    TEST_OVERRIDE_ARGS("unsigned long,size_type");

    type value = 0;
    size_type length = 0U;
    char* str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, "0");
    TestMemoryUtility<char>::SafeFree(str);

    char const* max_str = TestStringUtility::GetMaxUnsignedIntAsString(sizeof(type));
    value = TestMinMax<type>::max_value;
    length = 0;
    str = TestConverterUtility<type>::GetString(value, length);
    CHECK_NOT_NULL(str);
    CHECK_STRCMP(str, max_str);
    TestMemoryUtility<char>::SafeFree(str);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * TIGHTDB CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2012] TightDB Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of TightDB Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to TightDB Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from TightDB Incorporated.
 *
 **************************************************************************/
#ifndef TIGHTDB_COLUMN_BASIC_TPL_HPP
#define TIGHTDB_COLUMN_BASIC_TPL_HPP

#include <tightdb/query_engine.hpp>

namespace tightdb {

// Predeclarations from query_engine.hpp
class ParentNode;
template<class T, class F> class BasicNode;
template<class T> class SequentialGetter;

template<typename T>
BasicColumn<T>::BasicColumn(Allocator& alloc)
{
    m_array = new BasicArray<T>(NULL, 0, alloc);
}

template<typename T>
BasicColumn<T>::BasicColumn(size_t ref, ArrayParent* parent, size_t pndx, Allocator& alloc)
{
    const bool isNode = is_node_from_ref(ref, alloc);
    if (isNode)
        m_array = new Array(ref, parent, pndx, alloc);
    else
        m_array = new BasicArray<T>(ref, parent, pndx, alloc);
}

template<typename T>
BasicColumn<T>::~BasicColumn()
{
    if (IsNode())
        delete m_array;
    else
        delete static_cast<BasicArray<T>*>(m_array);
}

template<typename T>
void BasicColumn<T>::Destroy()
{
    if (IsNode())
        m_array->Destroy();
    else
        static_cast<BasicArray<T>*>(m_array)->Destroy();
}

template<typename T>
void BasicColumn<T>::UpdateRef(size_t ref)
{
    TIGHTDB_ASSERT(is_node_from_ref(ref, m_array->GetAllocator())); // Can only be called when creating node

    if (IsNode())
        m_array->UpdateRef(ref);
    else {
        ArrayParent* const parent = m_array->GetParent();
        const size_t pndx = m_array->GetParentNdx();

        // Replace the generic array with int array for node
        Array* array = new Array(ref, parent, pndx, m_array->GetAllocator());
        delete m_array;
        m_array = array;

        // Update ref in parent
        if (parent)
            parent->update_child_ref(pndx, ref);
    }
}

template<typename T>
bool BasicColumn<T>::is_empty() const TIGHTDB_NOEXCEPT
{
    if (IsNode()) {
        const Array offsets = NodeGetOffsets();
        return offsets.is_empty();
    }
    else {
        return static_cast<BasicArray<T>*>(m_array)->is_empty();
    }
}

template<typename T>
size_t BasicColumn<T>::Size() const TIGHTDB_NOEXCEPT
{
    if (IsNode())  {
        const Array offsets = NodeGetOffsets();
        const size_t size = offsets.is_empty() ? 0 : size_t(offsets.back());
        return size;
    }
    else {
        return m_array->size();
    }
}

template<typename T>
void BasicColumn<T>::Clear()
{
    if (m_array->IsNode()) {
        ArrayParent *const parent = m_array->GetParent();
        const size_t pndx = m_array->GetParentNdx();

        // Revert to generic array
        BasicArray<T>* array = new BasicArray<T>(parent, pndx, m_array->GetAllocator());
        if (parent)
            parent->update_child_ref(pndx, array->GetRef());

        // Remove original node
        m_array->Destroy();
        delete m_array;

        m_array = array;
    }
    else
        static_cast<BasicArray<T>*>(m_array)->Clear();
}

template<typename T>
void BasicColumn<T>::Resize(size_t ndx)
{
    TIGHTDB_ASSERT(!IsNode()); // currently only available on leaf level (used by b-tree code)
    TIGHTDB_ASSERT(ndx < Size());
    static_cast<BasicArray<T>*>(m_array)->Resize(ndx);
}

template<typename T>
T BasicColumn<T>::Get(size_t ndx) const TIGHTDB_NOEXCEPT
{
    TIGHTDB_ASSERT(ndx < Size());
    return BasicArray<T>::column_get(m_array, ndx);
}

template<typename T>
void BasicColumn<T>::Set(size_t ndx, T value)
{
    TIGHTDB_ASSERT(ndx < Size());
    TreeSet<T,BasicColumn<T> >(ndx, value);
}

template<typename T>
void BasicColumn<T>::add(T value)
{
    Insert(Size(), value);
}

template<typename T>
void BasicColumn<T>::Insert(size_t ndx, T value)
{
    TIGHTDB_ASSERT(ndx <= Size());
    TreeInsert<T, BasicColumn<T> >(ndx, value);
}

template<typename T>
void BasicColumn<T>::fill(size_t count)
{
    TIGHTDB_ASSERT(is_empty());

    // Fill column with default values
    // TODO: this is a very naive approach
    // we could speedup by creating full nodes directly
    for (size_t i = 0; i < count; ++i) {
        TreeInsert<T, BasicColumn<T> >(i, 0);
    }

#ifdef TIGHTDB_DEBUG
    Verify();
#endif
}

template<typename T>
bool BasicColumn<T>::compare(const BasicColumn& c) const
{
    const size_t n = Size();
    if (c.Size() != n)
        return false;
    for (size_t i=0; i<n; ++i) {
        const T v1 = Get(i);
        const T v2 = c.Get(i);
        if (v1 == v2)
            return false;
    }
    return true;
}

template<typename T>
void BasicColumn<T>::Delete(size_t ndx)
{
    TIGHTDB_ASSERT(ndx < Size());
    TreeDelete<T, BasicColumn<T> >(ndx);
}

template<typename T>
T BasicColumn<T>::LeafGet(size_t ndx) const TIGHTDB_NOEXCEPT
{
    return static_cast<BasicArray<T>*>(m_array)->Get(ndx);
}

template<typename T>
void BasicColumn<T>::LeafSet(size_t ndx, T value)
{
    static_cast<BasicArray<T>*>(m_array)->Set(ndx, value);
}

template<typename T>
void BasicColumn<T>::LeafInsert(size_t ndx, T value)
{
    static_cast<BasicArray<T>*>(m_array)->Insert(ndx, value);
}

template<typename T>
void BasicColumn<T>::LeafDelete(size_t ndx)
{
    static_cast<BasicArray<T>*>(m_array)->Delete(ndx);
}

#ifdef TIGHTDB_DEBUG

template<typename T>
void BasicColumn<T>::LeafToDot(std::ostream& out, const Array& array) const
{
    // Rebuild array to get correct type
    const size_t ref = array.GetRef();
    const BasicArray<T> newArray(ref, NULL, 0, array.GetAllocator());

    newArray.ToDot(out);
}

#endif // TIGHTDB_DEBUG

template<typename T> template<class F>
size_t BasicColumn<T>::LeafFind(T value, size_t start, size_t end) const
{
    return static_cast<BasicArray<T>*>(m_array)->find_first(value, start, end);
}

template<typename T>
void BasicColumn<T>::LeafFindAll(Array &result, T value, size_t add_offset, size_t start, size_t end) const
{
    return static_cast<BasicArray<T>*>(m_array)->find_all(result, value, add_offset, start, end);
}

template<typename T>
size_t BasicColumn<T>::find_first(T value, size_t start, size_t end) const
{
    TIGHTDB_ASSERT(value);

    return TreeFind<T, BasicColumn<T>, Equal>(value, start, end);
}

template<typename T>
void BasicColumn<T>::find_all(Array &result, T value, size_t start, size_t end) const
{
    TIGHTDB_ASSERT(value);

    TreeFindAll<T, BasicColumn<T> >(result, value, 0, start, end);
}

#if 1

template<typename T>
size_t BasicColumn<T>::count(T target) const
{
    return size_t(ColumnBase::aggregate<T, int64_t, act_Count, Equal>(target, 0, Size(), NULL));
}

template<typename T>
typename BasicColumn<T>::SumType BasicColumn<T>::sum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, SumType, act_Sum, None>(0, start, end, NULL);
}

template<typename T>
double BasicColumn<T>::average(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();
    size_t size = end - start;
    double sum1 = ColumnBase::aggregate<T, SumType, act_Sum, None>(0, start, end, NULL);
    double avg = sum1 / ( size == 0 ? 1 : size );
    return avg;
}

template<typename T>
T BasicColumn<T>::minimum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, T, act_Min, None>(0, start, end, NULL);
}

template<typename T>
T BasicColumn<T>::maximum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, T, act_Max, None>(0, start, end, NULL);
}

/*
template<typename T>
void BasicColumn<T>::sort(size_t start, size_t end)
{
    // TODO
    assert(0);
}
*/

#else

// Alternative 'naive' reference implementation - useful for reference performance testing.
// TODO: test performance of column aggregates

template<typename T>
size_t BasicColumn<T>::count(T target) const
{
    size_t count = 0;

    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = 0; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());

            count += col.count(target);
        }
    }
    else {
        count += ((BasicArray<T>*)m_array)->count(target);
    }
    return count;
}

template<typename T>
T BasicColumn<T>::sum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    double sum = 0;

    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());

            sum += col.sum(start, end);
        }
    }
    else {
        sum += ((BasicArray<T>*)m_array)->sum(start, end);
    }
    return sum;
}

template<typename T>
double BasicColumn<T>::average(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();
    size_t size = end - start;
    double sum2 = sum(start, end);
    double avg = sum2 / double( size == 0 ? 1 : size );
    return avg;
}

// #include <iostream>

template<typename T>
T BasicColumn<T>::minimum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    T min_val = T(987.0);
    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());
            T val = col.minimum(start, end);
            if (val < min_val || i == start) {
                //std::cout << "Min " << i << ": " << min_val << " new val: " << val << "\n";
                val = min_val;
            }
        }
    }
    else {
       // std::cout << "array-min before: " << min_val;
        ((BasicArray<T>*)m_array)->minimum(min_val, start, end);
       // std::cout << " after: " << min_val << "\n";
    }
    return min_val;
}

template<typename T>
T BasicColumn<T>::maximum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    T max_val = T(0.0);
    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());
            T val = col.maximum(start, end);
            if (val > max_val || i == start)
                val = max_val;
        }
    }
    else {
        ((BasicArray<T>*)m_array)->maximum(max_val, start, end);
    }
    return max_val;
}

#endif // reference implementation of aggregates

} // namespace tightdb

#endif // TIGHTDB_COLUMN_BASIC_TPL_HPP
<commit_msg>Fixed bug i float: was asserting in find_all and find_first when value was 0!<commit_after>/*************************************************************************
 *
 * TIGHTDB CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2012] TightDB Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of TightDB Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to TightDB Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from TightDB Incorporated.
 *
 **************************************************************************/
#ifndef TIGHTDB_COLUMN_BASIC_TPL_HPP
#define TIGHTDB_COLUMN_BASIC_TPL_HPP

#include <tightdb/query_engine.hpp>

namespace tightdb {

// Predeclarations from query_engine.hpp
class ParentNode;
template<class T, class F> class BasicNode;
template<class T> class SequentialGetter;

template<typename T>
BasicColumn<T>::BasicColumn(Allocator& alloc)
{
    m_array = new BasicArray<T>(NULL, 0, alloc);
}

template<typename T>
BasicColumn<T>::BasicColumn(size_t ref, ArrayParent* parent, size_t pndx, Allocator& alloc)
{
    const bool isNode = is_node_from_ref(ref, alloc);
    if (isNode)
        m_array = new Array(ref, parent, pndx, alloc);
    else
        m_array = new BasicArray<T>(ref, parent, pndx, alloc);
}

template<typename T>
BasicColumn<T>::~BasicColumn()
{
    if (IsNode())
        delete m_array;
    else
        delete static_cast<BasicArray<T>*>(m_array);
}

template<typename T>
void BasicColumn<T>::Destroy()
{
    if (IsNode())
        m_array->Destroy();
    else
        static_cast<BasicArray<T>*>(m_array)->Destroy();
}

template<typename T>
void BasicColumn<T>::UpdateRef(size_t ref)
{
    TIGHTDB_ASSERT(is_node_from_ref(ref, m_array->GetAllocator())); // Can only be called when creating node

    if (IsNode())
        m_array->UpdateRef(ref);
    else {
        ArrayParent* const parent = m_array->GetParent();
        const size_t pndx = m_array->GetParentNdx();

        // Replace the generic array with int array for node
        Array* array = new Array(ref, parent, pndx, m_array->GetAllocator());
        delete m_array;
        m_array = array;

        // Update ref in parent
        if (parent)
            parent->update_child_ref(pndx, ref);
    }
}

template<typename T>
bool BasicColumn<T>::is_empty() const TIGHTDB_NOEXCEPT
{
    if (IsNode()) {
        const Array offsets = NodeGetOffsets();
        return offsets.is_empty();
    }
    else {
        return static_cast<BasicArray<T>*>(m_array)->is_empty();
    }
}

template<typename T>
size_t BasicColumn<T>::Size() const TIGHTDB_NOEXCEPT
{
    if (IsNode())  {
        const Array offsets = NodeGetOffsets();
        const size_t size = offsets.is_empty() ? 0 : size_t(offsets.back());
        return size;
    }
    else {
        return m_array->size();
    }
}

template<typename T>
void BasicColumn<T>::Clear()
{
    if (m_array->IsNode()) {
        ArrayParent *const parent = m_array->GetParent();
        const size_t pndx = m_array->GetParentNdx();

        // Revert to generic array
        BasicArray<T>* array = new BasicArray<T>(parent, pndx, m_array->GetAllocator());
        if (parent)
            parent->update_child_ref(pndx, array->GetRef());

        // Remove original node
        m_array->Destroy();
        delete m_array;

        m_array = array;
    }
    else
        static_cast<BasicArray<T>*>(m_array)->Clear();
}

template<typename T>
void BasicColumn<T>::Resize(size_t ndx)
{
    TIGHTDB_ASSERT(!IsNode()); // currently only available on leaf level (used by b-tree code)
    TIGHTDB_ASSERT(ndx < Size());
    static_cast<BasicArray<T>*>(m_array)->Resize(ndx);
}

template<typename T>
T BasicColumn<T>::Get(size_t ndx) const TIGHTDB_NOEXCEPT
{
    TIGHTDB_ASSERT(ndx < Size());
    return BasicArray<T>::column_get(m_array, ndx);
}

template<typename T>
void BasicColumn<T>::Set(size_t ndx, T value)
{
    TIGHTDB_ASSERT(ndx < Size());
    TreeSet<T,BasicColumn<T> >(ndx, value);
}

template<typename T>
void BasicColumn<T>::add(T value)
{
    Insert(Size(), value);
}

template<typename T>
void BasicColumn<T>::Insert(size_t ndx, T value)
{
    TIGHTDB_ASSERT(ndx <= Size());
    TreeInsert<T, BasicColumn<T> >(ndx, value);
}

template<typename T>
void BasicColumn<T>::fill(size_t count)
{
    TIGHTDB_ASSERT(is_empty());

    // Fill column with default values
    // TODO: this is a very naive approach
    // we could speedup by creating full nodes directly
    for (size_t i = 0; i < count; ++i) {
        TreeInsert<T, BasicColumn<T> >(i, 0);
    }

#ifdef TIGHTDB_DEBUG
    Verify();
#endif
}

template<typename T>
bool BasicColumn<T>::compare(const BasicColumn& c) const
{
    const size_t n = Size();
    if (c.Size() != n)
        return false;
    for (size_t i=0; i<n; ++i) {
        const T v1 = Get(i);
        const T v2 = c.Get(i);
        if (v1 == v2)
            return false;
    }
    return true;
}

template<typename T>
void BasicColumn<T>::Delete(size_t ndx)
{
    TIGHTDB_ASSERT(ndx < Size());
    TreeDelete<T, BasicColumn<T> >(ndx);
}

template<typename T>
T BasicColumn<T>::LeafGet(size_t ndx) const TIGHTDB_NOEXCEPT
{
    return static_cast<BasicArray<T>*>(m_array)->Get(ndx);
}

template<typename T>
void BasicColumn<T>::LeafSet(size_t ndx, T value)
{
    static_cast<BasicArray<T>*>(m_array)->Set(ndx, value);
}

template<typename T>
void BasicColumn<T>::LeafInsert(size_t ndx, T value)
{
    static_cast<BasicArray<T>*>(m_array)->Insert(ndx, value);
}

template<typename T>
void BasicColumn<T>::LeafDelete(size_t ndx)
{
    static_cast<BasicArray<T>*>(m_array)->Delete(ndx);
}

#ifdef TIGHTDB_DEBUG

template<typename T>
void BasicColumn<T>::LeafToDot(std::ostream& out, const Array& array) const
{
    // Rebuild array to get correct type
    const size_t ref = array.GetRef();
    const BasicArray<T> newArray(ref, NULL, 0, array.GetAllocator());

    newArray.ToDot(out);
}

#endif // TIGHTDB_DEBUG

template<typename T> template<class F>
size_t BasicColumn<T>::LeafFind(T value, size_t start, size_t end) const
{
    return static_cast<BasicArray<T>*>(m_array)->find_first(value, start, end);
}

template<typename T>
void BasicColumn<T>::LeafFindAll(Array &result, T value, size_t add_offset, size_t start, size_t end) const
{
    return static_cast<BasicArray<T>*>(m_array)->find_all(result, value, add_offset, start, end);
}

template<typename T>
size_t BasicColumn<T>::find_first(T value, size_t start, size_t end) const
{
    return TreeFind<T, BasicColumn<T>, Equal>(value, start, end);
}

template<typename T>
void BasicColumn<T>::find_all(Array &result, T value, size_t start, size_t end) const
{
    TreeFindAll<T, BasicColumn<T> >(result, value, 0, start, end);
}

#if 1

template<typename T>
size_t BasicColumn<T>::count(T target) const
{
    return size_t(ColumnBase::aggregate<T, int64_t, act_Count, Equal>(target, 0, Size(), NULL));
}

template<typename T>
typename BasicColumn<T>::SumType BasicColumn<T>::sum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, SumType, act_Sum, None>(0, start, end, NULL);
}

template<typename T>
double BasicColumn<T>::average(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();
    size_t size = end - start;
    double sum1 = ColumnBase::aggregate<T, SumType, act_Sum, None>(0, start, end, NULL);
    double avg = sum1 / ( size == 0 ? 1 : size );
    return avg;
}

template<typename T>
T BasicColumn<T>::minimum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, T, act_Min, None>(0, start, end, NULL);
}

template<typename T>
T BasicColumn<T>::maximum(size_t start, size_t end) const
{
    return ColumnBase::aggregate<T, T, act_Max, None>(0, start, end, NULL);
}

/*
template<typename T>
void BasicColumn<T>::sort(size_t start, size_t end)
{
    // TODO
    assert(0);
}
*/

#else

// Alternative 'naive' reference implementation - useful for reference performance testing.
// TODO: test performance of column aggregates

template<typename T>
size_t BasicColumn<T>::count(T target) const
{
    size_t count = 0;

    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = 0; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());

            count += col.count(target);
        }
    }
    else {
        count += ((BasicArray<T>*)m_array)->count(target);
    }
    return count;
}

template<typename T>
T BasicColumn<T>::sum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    double sum = 0;

    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());

            sum += col.sum(start, end);
        }
    }
    else {
        sum += ((BasicArray<T>*)m_array)->sum(start, end);
    }
    return sum;
}

template<typename T>
double BasicColumn<T>::average(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();
    size_t size = end - start;
    double sum2 = sum(start, end);
    double avg = sum2 / double( size == 0 ? 1 : size );
    return avg;
}

// #include <iostream>

template<typename T>
T BasicColumn<T>::minimum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    T min_val = T(987.0);
    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());
            T val = col.minimum(start, end);
            if (val < min_val || i == start) {
                //std::cout << "Min " << i << ": " << min_val << " new val: " << val << "\n";
                val = min_val;
            }
        }
    }
    else {
       // std::cout << "array-min before: " << min_val;
        ((BasicArray<T>*)m_array)->minimum(min_val, start, end);
       // std::cout << " after: " << min_val << "\n";
    }
    return min_val;
}

template<typename T>
T BasicColumn<T>::maximum(size_t start, size_t end) const
{
    if (end == size_t(-1))
        end = Size();

    T max_val = T(0.0);
    if (m_array->IsNode()) {
        const Array refs = NodeGetRefs();
        const size_t n = refs.size();

        for (size_t i = start; i < n; ++i) {
            const size_t ref = refs.GetAsRef(i);
            const BasicColumn<T> col(ref, NULL, 0, m_array->GetAllocator());
            T val = col.maximum(start, end);
            if (val > max_val || i == start)
                val = max_val;
        }
    }
    else {
        ((BasicArray<T>*)m_array)->maximum(max_val, start, end);
    }
    return max_val;
}

#endif // reference implementation of aggregates

} // namespace tightdb

#endif // TIGHTDB_COLUMN_BASIC_TPL_HPP
<|endoftext|>


<commit_before>#include <cstring>
#include <fstream>
#include <glob.h>
#include <iostream>

#include "ast.h"
#include "bpftrace.h"
#include "log.h"
#include "struct.h"
#include "tracepoint_format_parser.h"

namespace bpftrace {

std::set<std::string> TracepointFormatParser::struct_list;

bool TracepointFormatParser::parse(ast::Program *program, BPFtrace &bpftrace)
{
  std::vector<ast::Probe*> probes_with_tracepoint;
  for (ast::Probe *probe : *program->probes)
    for (ast::AttachPoint *ap : *probe->attach_points)
      if (ap->provider == "tracepoint") {
        probes_with_tracepoint.push_back(probe);
        continue;
      }

  if (probes_with_tracepoint.empty())
    return true;

  ast::TracepointArgsVisitor n{};
  if (!bpftrace.btf_.has_data())
    program->c_definitions += "#include <linux/types.h>\n";
  for (ast::Probe *probe : probes_with_tracepoint)
  {
    n.visit(*probe);

    for (ast::AttachPoint *ap : *probe->attach_points)
    {
      if (ap->provider == "tracepoint")
      {
        std::string &category = ap->target;
        std::string &event_name = ap->func;
        std::string format_file_path = "/sys/kernel/debug/tracing/events/" + category + "/" + event_name + "/format";
        glob_t glob_result;

        if (has_wildcard(category) || has_wildcard(event_name))
        {
          // tracepoint wildcard expansion, part 1 of 3. struct definitions.
          memset(&glob_result, 0, sizeof(glob_result));
          int ret = glob(format_file_path.c_str(), 0, NULL, &glob_result);
          if (ret != 0)
          {
            if (ret == GLOB_NOMATCH)
            {
              LOG(ERROR, ap->loc, std::cerr)
                  << "tracepoints not found: " << category << ":" << event_name;
              // helper message:
              if (category == "syscall")
                LOG(ERROR, ap->loc, std::cerr)
                    << "Did you mean syscalls:" << event_name << "?";
              if (bt_verbose) {
                LOG(ERROR) << strerror(errno) << ": " << format_file_path;
              }
              return false;
            }
            else
            {
              // unexpected error
              LOG(ERROR, ap->loc, std::cerr) << std::string(strerror(errno));
              return false;
            }
          }

          if (probe->tp_args_structs_level <= 0)
            continue;

          for (size_t i = 0; i < glob_result.gl_pathc; ++i) {
            std::string filename(glob_result.gl_pathv[i]);
            std::ifstream format_file(filename);
            std::string prefix("/sys/kernel/debug/tracing/events/");
            size_t pos = prefix.length();
            std::string real_category = filename.substr(
                pos, filename.find('/', pos) - pos);
            pos = prefix.length() + real_category.length() + 1;
            std::string real_event = filename.substr(
                pos, filename.length() - std::string("/format").length() - pos);

            // Check to avoid adding the same struct more than once to definitions
            std::string struct_name = get_struct_name(real_category,
                                                      real_event);
            if (!TracepointFormatParser::struct_list.count(struct_name))
            {
              program->c_definitions += get_tracepoint_struct(
                  format_file, real_category, real_event, bpftrace);
              TracepointFormatParser::struct_list.insert(struct_name);
            }
          }
          globfree(&glob_result);
        }
        else
        {
          // single tracepoint
          std::ifstream format_file(format_file_path.c_str());
          if (format_file.fail())
          {
            // Errno might get clobbered by LOG().
            int saved_errno = errno;

            LOG(ERROR, ap->loc, std::cerr)
                << "tracepoint not found: " << category << ":" << event_name;
            // helper message:
            if (category == "syscall")
              LOG(WARNING, ap->loc, std::cerr)
                  << "Did you mean syscalls:" << event_name << "?";
            if (bt_verbose) {
              // Having the location info isn't really useful here, so no
              // bpftrace.error
              LOG(ERROR) << strerror(saved_errno) << ": " << format_file_path;
            }
            return false;
          }

          if (probe->tp_args_structs_level <= 0)
            continue;

          // Check to avoid adding the same struct more than once to definitions
          std::string struct_name = get_struct_name(category, event_name);
          if (TracepointFormatParser::struct_list.insert(struct_name).second)
            program->c_definitions += get_tracepoint_struct(
                format_file, category, event_name, bpftrace);
        }
      }
    }
  }
  return true;
}

std::string TracepointFormatParser::get_struct_name(const std::string &category, const std::string &event_name)
{
  return "struct _tracepoint_" + category + "_" + event_name;
}

std::string TracepointFormatParser::get_struct_name(const std::string &probe_id)
{
  // probe_id has format category:event
  std::string event_name = probe_id;
  std::string category = erase_prefix(event_name);
  return get_struct_name(category, event_name);
}

std::string TracepointFormatParser::parse_field(const std::string &line,
                                                int *last_offset,
                                                BPFtrace &bpftrace)
{
  std::string extra = "";

  auto field_pos = line.find("field:");
  if (field_pos == std::string::npos)
    return "";

  auto field_semi_pos = line.find(';', field_pos);
  if (field_semi_pos == std::string::npos)
    return "";

  auto offset_pos = line.find("offset:", field_semi_pos);
  if (offset_pos == std::string::npos)
    return "";

  auto offset_semi_pos = line.find(';', offset_pos);
  if (offset_semi_pos == std::string::npos)
    return "";

  auto size_pos = line.find("size:", offset_semi_pos);
  if (size_pos == std::string::npos)
    return "";

  auto size_semi_pos = line.find(';', size_pos);
  if (size_semi_pos == std::string::npos)
    return "";

  int size = std::stoi(line.substr(size_pos + 5, size_semi_pos - size_pos - 5));
  int offset = std::stoi(
      line.substr(offset_pos + 7, offset_semi_pos - offset_pos - 7));

  // If there'a gap between last field and this one,
  // generate padding fields
  if (offset && *last_offset)
  {
    int i, gap = offset - *last_offset;

    for (i = 0; i < gap; i++)
    {
      extra += "  char __pad_" + std::to_string(offset - gap + i) + ";\n";
    }
  }

  *last_offset = offset + size;

  std::string field = line.substr(field_pos + 6, field_semi_pos - field_pos - 6);
  auto field_type_end_pos = field.find_last_of("\t ");
  if (field_type_end_pos == std::string::npos)
    return "";
  std::string field_type = field.substr(0, field_type_end_pos);
  std::string field_name = field.substr(field_type_end_pos+1);

  if (field_type.find("__data_loc") != std::string::npos)
  {
    // Note that the type here (ie `int`) does not matter. Later during parse
    // time the parser will rewrite this field type to a u64 so that it can
    // hold the pointer to the actual location of the data.
    field_type = R"_(__attribute__((annotate("tp_data_loc"))) int)_";
  }

  // Only adjust field types for non-arrays
  if (field_name.find("[") == std::string::npos)
    field_type = adjust_integer_types(field_type, size);

  // With --btf on, we try not to use any header files, including
  // <linux/types.h>. That means we must request all the types we need
  // from BTF. Note we don't need to gate this on --btf because the
  // expensive type reslution is already gated on --btf (adding to a set
  // is cheap).
  bpftrace.btf_set_.emplace(field_type);

  return extra + "  " + field_type + " " + field_name + ";\n";
}

std::string TracepointFormatParser::adjust_integer_types(const std::string &field_type, int size)
{
  std::string new_type = field_type;
  // Adjust integer fields to correctly sized types
  if (size == 8)
  {
    if (field_type == "int")
      new_type = "s64";
    if (field_type == "unsigned int" || field_type == "unsigned" ||
        field_type == "u32" || field_type == "pid_t" ||
        field_type == "uid_t" || field_type == "gid_t")
      new_type = "u64";
  }

  return new_type;
}

std::string TracepointFormatParser::get_tracepoint_struct(
    std::istream &format_file,
    const std::string &category,
    const std::string &event_name,
    BPFtrace &bpftrace)
{
  std::string format_struct = get_struct_name(category, event_name) + "\n{\n";
  int last_offset = 0;

  for (std::string line; getline(format_file, line); )
  {
    format_struct += parse_field(line, &last_offset, bpftrace);
  }

  format_struct += "};\n";
  return format_struct;
}

} // namespace bpftrace
<commit_msg>Fix memory leak in tracepoint parser<commit_after>#include <cstring>
#include <fstream>
#include <glob.h>
#include <iostream>

#include "ast.h"
#include "bpftrace.h"
#include "log.h"
#include "struct.h"
#include "tracepoint_format_parser.h"

namespace bpftrace {

std::set<std::string> TracepointFormatParser::struct_list;

bool TracepointFormatParser::parse(ast::Program *program, BPFtrace &bpftrace)
{
  std::vector<ast::Probe*> probes_with_tracepoint;
  for (ast::Probe *probe : *program->probes)
    for (ast::AttachPoint *ap : *probe->attach_points)
      if (ap->provider == "tracepoint") {
        probes_with_tracepoint.push_back(probe);
        continue;
      }

  if (probes_with_tracepoint.empty())
    return true;

  ast::TracepointArgsVisitor n{};
  if (!bpftrace.btf_.has_data())
    program->c_definitions += "#include <linux/types.h>\n";
  for (ast::Probe *probe : probes_with_tracepoint)
  {
    n.visit(*probe);

    for (ast::AttachPoint *ap : *probe->attach_points)
    {
      if (ap->provider == "tracepoint")
      {
        std::string &category = ap->target;
        std::string &event_name = ap->func;
        std::string format_file_path = "/sys/kernel/debug/tracing/events/" + category + "/" + event_name + "/format";
        glob_t glob_result;

        if (has_wildcard(category) || has_wildcard(event_name))
        {
          // tracepoint wildcard expansion, part 1 of 3. struct definitions.
          memset(&glob_result, 0, sizeof(glob_result));
          int ret = glob(format_file_path.c_str(), 0, NULL, &glob_result);
          if (ret != 0)
          {
            if (ret == GLOB_NOMATCH)
            {
              LOG(ERROR, ap->loc, std::cerr)
                  << "tracepoints not found: " << category << ":" << event_name;
              // helper message:
              if (category == "syscall")
                LOG(ERROR, ap->loc, std::cerr)
                    << "Did you mean syscalls:" << event_name << "?";
              if (bt_verbose) {
                LOG(ERROR) << strerror(errno) << ": " << format_file_path;
              }
              return false;
            }
            else
            {
              // unexpected error
              LOG(ERROR, ap->loc, std::cerr) << std::string(strerror(errno));
              return false;
            }
          }

          if (probe->tp_args_structs_level <= 0)
          {
            globfree(&glob_result);
            continue;
          }

          for (size_t i = 0; i < glob_result.gl_pathc; ++i) {
            std::string filename(glob_result.gl_pathv[i]);
            std::ifstream format_file(filename);
            std::string prefix("/sys/kernel/debug/tracing/events/");
            size_t pos = prefix.length();
            std::string real_category = filename.substr(
                pos, filename.find('/', pos) - pos);
            pos = prefix.length() + real_category.length() + 1;
            std::string real_event = filename.substr(
                pos, filename.length() - std::string("/format").length() - pos);

            // Check to avoid adding the same struct more than once to definitions
            std::string struct_name = get_struct_name(real_category,
                                                      real_event);
            if (!TracepointFormatParser::struct_list.count(struct_name))
            {
              program->c_definitions += get_tracepoint_struct(
                  format_file, real_category, real_event, bpftrace);
              TracepointFormatParser::struct_list.insert(struct_name);
            }
          }
          globfree(&glob_result);
        }
        else
        {
          // single tracepoint
          std::ifstream format_file(format_file_path.c_str());
          if (format_file.fail())
          {
            // Errno might get clobbered by LOG().
            int saved_errno = errno;

            LOG(ERROR, ap->loc, std::cerr)
                << "tracepoint not found: " << category << ":" << event_name;
            // helper message:
            if (category == "syscall")
              LOG(WARNING, ap->loc, std::cerr)
                  << "Did you mean syscalls:" << event_name << "?";
            if (bt_verbose) {
              // Having the location info isn't really useful here, so no
              // bpftrace.error
              LOG(ERROR) << strerror(saved_errno) << ": " << format_file_path;
            }
            return false;
          }

          if (probe->tp_args_structs_level <= 0)
            continue;

          // Check to avoid adding the same struct more than once to definitions
          std::string struct_name = get_struct_name(category, event_name);
          if (TracepointFormatParser::struct_list.insert(struct_name).second)
            program->c_definitions += get_tracepoint_struct(
                format_file, category, event_name, bpftrace);
        }
      }
    }
  }
  return true;
}

std::string TracepointFormatParser::get_struct_name(const std::string &category, const std::string &event_name)
{
  return "struct _tracepoint_" + category + "_" + event_name;
}

std::string TracepointFormatParser::get_struct_name(const std::string &probe_id)
{
  // probe_id has format category:event
  std::string event_name = probe_id;
  std::string category = erase_prefix(event_name);
  return get_struct_name(category, event_name);
}

std::string TracepointFormatParser::parse_field(const std::string &line,
                                                int *last_offset,
                                                BPFtrace &bpftrace)
{
  std::string extra = "";

  auto field_pos = line.find("field:");
  if (field_pos == std::string::npos)
    return "";

  auto field_semi_pos = line.find(';', field_pos);
  if (field_semi_pos == std::string::npos)
    return "";

  auto offset_pos = line.find("offset:", field_semi_pos);
  if (offset_pos == std::string::npos)
    return "";

  auto offset_semi_pos = line.find(';', offset_pos);
  if (offset_semi_pos == std::string::npos)
    return "";

  auto size_pos = line.find("size:", offset_semi_pos);
  if (size_pos == std::string::npos)
    return "";

  auto size_semi_pos = line.find(';', size_pos);
  if (size_semi_pos == std::string::npos)
    return "";

  int size = std::stoi(line.substr(size_pos + 5, size_semi_pos - size_pos - 5));
  int offset = std::stoi(
      line.substr(offset_pos + 7, offset_semi_pos - offset_pos - 7));

  // If there'a gap between last field and this one,
  // generate padding fields
  if (offset && *last_offset)
  {
    int i, gap = offset - *last_offset;

    for (i = 0; i < gap; i++)
    {
      extra += "  char __pad_" + std::to_string(offset - gap + i) + ";\n";
    }
  }

  *last_offset = offset + size;

  std::string field = line.substr(field_pos + 6, field_semi_pos - field_pos - 6);
  auto field_type_end_pos = field.find_last_of("\t ");
  if (field_type_end_pos == std::string::npos)
    return "";
  std::string field_type = field.substr(0, field_type_end_pos);
  std::string field_name = field.substr(field_type_end_pos+1);

  if (field_type.find("__data_loc") != std::string::npos)
  {
    // Note that the type here (ie `int`) does not matter. Later during parse
    // time the parser will rewrite this field type to a u64 so that it can
    // hold the pointer to the actual location of the data.
    field_type = R"_(__attribute__((annotate("tp_data_loc"))) int)_";
  }

  // Only adjust field types for non-arrays
  if (field_name.find("[") == std::string::npos)
    field_type = adjust_integer_types(field_type, size);

  // With --btf on, we try not to use any header files, including
  // <linux/types.h>. That means we must request all the types we need
  // from BTF. Note we don't need to gate this on --btf because the
  // expensive type reslution is already gated on --btf (adding to a set
  // is cheap).
  bpftrace.btf_set_.emplace(field_type);

  return extra + "  " + field_type + " " + field_name + ";\n";
}

std::string TracepointFormatParser::adjust_integer_types(const std::string &field_type, int size)
{
  std::string new_type = field_type;
  // Adjust integer fields to correctly sized types
  if (size == 8)
  {
    if (field_type == "int")
      new_type = "s64";
    if (field_type == "unsigned int" || field_type == "unsigned" ||
        field_type == "u32" || field_type == "pid_t" ||
        field_type == "uid_t" || field_type == "gid_t")
      new_type = "u64";
  }

  return new_type;
}

std::string TracepointFormatParser::get_tracepoint_struct(
    std::istream &format_file,
    const std::string &category,
    const std::string &event_name,
    BPFtrace &bpftrace)
{
  std::string format_struct = get_struct_name(category, event_name) + "\n{\n";
  int last_offset = 0;

  for (std::string line; getline(format_file, line); )
  {
    format_struct += parse_field(line, &last_offset, bpftrace);
  }

  format_struct += "};\n";
  return format_struct;
}

} // namespace bpftrace
<|endoftext|>