view test/hotspot/gtest/jfr/test_networkUtilization.cpp @ 51513:d90c3cbf13df

8003209: JFR events for network utilization Reviewed-by: mgronlun, egahlin
author rwestberg
date Thu, 28 Jun 2018 15:06:55 +0200
parents
children e8d55141afd2
line wrap: on
line source
/*
 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#include "precompiled.hpp"

// This test performs mocking of certain JVM functionality. This works by
// including the source file under test inside an anonymous namespace (which
// prevents linking conflicts) with the mocked symbols redefined.

// The include list should mirror the one found in the included source file -
// with the ones that should pick up the mocks removed. Those should be included
// later after the mocks have been defined.

#include "logging/log.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/metadata/jfrSerializer.hpp"
#include "jfr/periodic/jfrOSInterface.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "runtime/os_perf.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"

#include "unittest.hpp"

#include <vector>
#include <list>
#include <map>

namespace {

  class MockFastUnorderedElapsedCounterSource : public ::FastUnorderedElapsedCounterSource {
  public:
    static jlong current_ticks;
    static Type now() {
      return current_ticks;
    }
    static uint64_t nanoseconds(Type value) {
      return value;
    }
  };

  typedef TimeInstant<CounterRepresentation, MockFastUnorderedElapsedCounterSource> JfrTicks;
  typedef TimeInterval<CounterRepresentation, MockFastUnorderedElapsedCounterSource> JfrTickspan;

  class MockJfrCheckpointWriter {
  public:
    traceid current;
    std::map<traceid, std::string> ids;

    const JfrCheckpointContext context() const {
      return JfrCheckpointContext();
    }
    intptr_t reserve(size_t size) {
      return 0;
    }
    void write_key(traceid id) {
      current = id;
    }
    void write(const char* data) {
      ids[current] = data;
    }
    void set_context(const JfrCheckpointContext ctx) { }
    void write_count(u4 nof_entries, jlong offset) { }
  };

  class MockJfrSerializer {
  public:
    static MockJfrSerializer* current;

    static bool register_serializer(JfrTypeId id, bool require_safepoint, bool permit_cache, MockJfrSerializer* serializer) {
      current = serializer;
      return true;
    }

    virtual void serialize(MockJfrCheckpointWriter& writer) = 0;
  };

  MockJfrSerializer* MockJfrSerializer::current;

  class MockEventNetworkUtilization : public ::EventNetworkUtilization
  {
  public:
    std::string iface;
    s8 readRate;
    s8 writeRate;
    static std::vector<MockEventNetworkUtilization> committed;
    MockJfrCheckpointWriter writer;

  public:
    MockEventNetworkUtilization(EventStartTime timing=TIMED) :
    ::EventNetworkUtilization(timing) {
    }

    void set_networkInterface(traceid new_value) {
      MockJfrSerializer::current->serialize(writer);
      iface = writer.ids[new_value];
    }
    void set_readRate(s8 new_value) {
      readRate = new_value;
    }
    void set_writeRate(s8 new_value) {
      writeRate = new_value;
    }

    void commit() {
      committed.push_back(*this);
    }

    void set_starttime(const JfrTicks& time) {}

    void set_endtime(const JfrTicks& time) {}

    static const MockEventNetworkUtilization& get_committed(const std::string& name) {
      static MockEventNetworkUtilization placeholder;
      for (std::vector<MockEventNetworkUtilization>::const_iterator i = committed.begin();
           i != committed.end();
           ++i) {
        if (name == i->iface) {
          return *i;
        }
      }
      return placeholder;
    }
  };

  std::vector<MockEventNetworkUtilization> MockEventNetworkUtilization::committed;

  jlong MockFastUnorderedElapsedCounterSource::current_ticks;

  struct MockNetworkInterface {
    std::string name;
    uint64_t bytes_in;
    uint64_t bytes_out;
    MockNetworkInterface(std::string name, uint64_t bytes_in, uint64_t bytes_out)
    : name(name),
    bytes_in(bytes_in),
    bytes_out(bytes_out) {

    }
    bool operator==(const MockNetworkInterface& rhs) const {
      return name == rhs.name;
    }
  };

  class NetworkInterface : public ::NetworkInterface {
  public:
    NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next)
    : ::NetworkInterface(name, bytes_in, bytes_out, next) {
    }
    NetworkInterface* next(void) const {
      return reinterpret_cast<NetworkInterface*>(::NetworkInterface::next());
    }
  };

  class MockJfrOSInterface {
    static std::list<MockNetworkInterface> _interfaces;

  public:
    MockJfrOSInterface() {
    }
    static int network_utilization(NetworkInterface** network_interfaces) {
      *network_interfaces = NULL;
      for (std::list<MockNetworkInterface>::const_iterator i = _interfaces.begin();
           i != _interfaces.end();
           ++i) {
        NetworkInterface* cur = new NetworkInterface(i->name.c_str(), i->bytes_in, i->bytes_out, *network_interfaces);
        *network_interfaces = cur;
      }
      return OS_OK;
    }
    static MockNetworkInterface& add_interface(const std::string& name) {
      MockNetworkInterface iface(name, 0, 0);
      _interfaces.push_back(iface);
      return _interfaces.back();
    }
    static void remove_interface(const MockNetworkInterface& iface) {
      _interfaces.remove(iface);
    }
    static void clear_interfaces() {
      _interfaces.clear();
    }
  };

  std::list<MockNetworkInterface> MockJfrOSInterface::_interfaces;

// Reincluding source files in the anonymous namespace unfortunately seems to
// behave strangely with precompiled headers (only when using gcc though)
#ifndef DONT_USE_PRECOMPILED_HEADER
#define DONT_USE_PRECOMPILED_HEADER
#endif

#define EventNetworkUtilization MockEventNetworkUtilization
#define FastUnorderedElapsedCounterSource MockFastUnorderedElapsedCounterSource
#define JfrOSInterface MockJfrOSInterface
#define JfrSerializer MockJfrSerializer
#define JfrCheckpointWriter MockJfrCheckpointWriter

#include "jfr/periodic/jfrNetworkUtilization.hpp"
#include "jfr/periodic/jfrNetworkUtilization.cpp"

#undef EventNetworkUtilization
#undef FastUnorderedElapsedCounterSource
#undef JfrOSInterface
#undef JfrSerializer
#undef JfrCheckpointWriter

} // anonymous namespace

class JfrTestNetworkUtilization : public ::testing::Test {
protected:
  void SetUp() {
    MockEventNetworkUtilization::committed.clear();
    MockJfrOSInterface::clear_interfaces();
    // Ensure that tests are separated in time
    MockFastUnorderedElapsedCounterSource::current_ticks += 1 * NANOSECS_PER_SEC;
  }

  void TearDown() {
    JfrNetworkUtilization::destroy();
  }
};

TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionBasic) {

  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
  JfrNetworkUtilization::send_events();
  ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());

  eth0.bytes_in += 10;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(1u, MockEventNetworkUtilization::committed.size());
  MockEventNetworkUtilization& e = MockEventNetworkUtilization::committed[0];
  EXPECT_EQ(5, e.readRate);
  EXPECT_EQ(0, e.writeRate);
  EXPECT_STREQ("eth0", e.iface.c_str());
}

TEST_VM_F(JfrTestNetworkUtilization, RequestFunctionMultiple) {

  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
  MockNetworkInterface& ppp0 = MockJfrOSInterface::add_interface("ppp0");
  JfrNetworkUtilization::send_events();
  ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());

  eth0.bytes_in += 10;
  eth1.bytes_in += 100;
  ppp0.bytes_out += 50;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(3u, MockEventNetworkUtilization::committed.size());
  const MockEventNetworkUtilization& eth0_event = MockEventNetworkUtilization::get_committed("eth0");
  const MockEventNetworkUtilization& eth1_event = MockEventNetworkUtilization::get_committed("eth1");
  const MockEventNetworkUtilization& ppp0_event = MockEventNetworkUtilization::get_committed("ppp0");

  EXPECT_EQ(5, eth0_event.readRate);
  EXPECT_EQ(0, eth0_event.writeRate);
  EXPECT_STREQ("eth0", eth0_event.iface.c_str());

  EXPECT_EQ(50, eth1_event.readRate);
  EXPECT_EQ(0, eth1_event.writeRate);
  EXPECT_STREQ("eth1", eth1_event.iface.c_str());

  EXPECT_EQ(0, ppp0_event.readRate);
  EXPECT_EQ(25, ppp0_event.writeRate);
  EXPECT_STREQ("ppp0", ppp0_event.iface.c_str());
}

TEST_VM_F(JfrTestNetworkUtilization, InterfaceRemoved) {
  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
  MockNetworkInterface& eth1 = MockJfrOSInterface::add_interface("eth1");
  JfrNetworkUtilization::send_events();
  ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());

  eth0.bytes_in += 10;
  eth1.bytes_in += 20;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(2u, MockEventNetworkUtilization::committed.size());
  const MockEventNetworkUtilization& eth0_event = MockEventNetworkUtilization::get_committed("eth0");
  const MockEventNetworkUtilization& eth1_event = MockEventNetworkUtilization::get_committed("eth1");

  EXPECT_EQ(5, eth0_event.readRate);
  EXPECT_EQ(0, eth0_event.writeRate);
  EXPECT_STREQ("eth0", eth0_event.iface.c_str());

  EXPECT_EQ(10, eth1_event.readRate);
  EXPECT_EQ(0, eth1_event.writeRate);
  EXPECT_STREQ("eth1", eth1_event.iface.c_str());

  MockJfrOSInterface::remove_interface(eth0);
  MockEventNetworkUtilization::committed.clear();

  eth1.bytes_in += 10;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;
  JfrNetworkUtilization::send_events();
  ASSERT_EQ(1u, MockEventNetworkUtilization::committed.size());
  const MockEventNetworkUtilization& eth1_event_v2 = MockEventNetworkUtilization::get_committed("eth1");

  EXPECT_EQ(5, eth1_event_v2.readRate);
  EXPECT_EQ(0, eth1_event_v2.writeRate);
  EXPECT_STREQ("eth1", eth1_event_v2.iface.c_str());
}

TEST_VM_F(JfrTestNetworkUtilization, InterfaceReset) {
  MockNetworkInterface& eth0 = MockJfrOSInterface::add_interface("eth0");
  JfrNetworkUtilization::send_events();
  ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());

  eth0.bytes_in += 10;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(1u, MockEventNetworkUtilization::committed.size());
  const MockEventNetworkUtilization& event = MockEventNetworkUtilization::committed[0];
  EXPECT_EQ(5, event.readRate);
  EXPECT_EQ(0, event.writeRate);
  EXPECT_STREQ("eth0", event.iface.c_str());

  eth0.bytes_in = 0;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;
  MockEventNetworkUtilization::committed.clear();

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(0u, MockEventNetworkUtilization::committed.size());

  eth0.bytes_in = 10;
  MockFastUnorderedElapsedCounterSource::current_ticks += 2 * NANOSECS_PER_SEC;

  JfrNetworkUtilization::send_events();
  ASSERT_EQ(1u, MockEventNetworkUtilization::committed.size());
  const MockEventNetworkUtilization& event_v2 = MockEventNetworkUtilization::committed[0];
  EXPECT_EQ(5, event_v2.readRate);
  EXPECT_EQ(0, event_v2.writeRate);
  EXPECT_STREQ("eth0", event_v2.iface.c_str());
}