# HG changeset patch # User rwestberg # Date 1530191215 -7200 # Node ID d90c3cbf13df17301194314a3840ddf79d5f0263 # Parent fb7800b66c92dda08d2ddf6958250dc8b1e5c6e7 8003209: JFR events for network utilization Reviewed-by: mgronlun, egahlin diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/aix/os_perf_aix.cpp --- a/src/hotspot/os/aix/os_perf_aix.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/os/aix/os_perf_aix.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -1041,3 +1041,49 @@ cpu_info = *_cpu_info; // shallow copy assignment return OS_OK; } + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + return FUNCTIONALITY_NOT_IMPLEMENTED; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/bsd/os_perf_bsd.cpp --- a/src/hotspot/os/bsd/os_perf_bsd.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/os/bsd/os_perf_bsd.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -34,6 +34,10 @@ #include #include #include + #include + #include + #include + #include #endif static const double NANOS_PER_SEC = 1000000000.0; @@ -403,3 +407,85 @@ cpu_info = *_cpu_info; // shallow copy assignment return OS_OK; } + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const { + size_t len; + int mib[] = {CTL_NET, PF_ROUTE, /* protocol number */ 0, /* address family */ 0, NET_RT_IFLIST2, /* NET_RT_FLAGS mask*/ 0}; + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0) != 0) { + return OS_ERR; + } + uint8_t* buf = NEW_RESOURCE_ARRAY(uint8_t, len); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &len, NULL, 0) != 0) { + return OS_ERR; + } + + size_t index = 0; + NetworkInterface* ret = NULL; + while (index < len) { + if_msghdr* msghdr = reinterpret_cast(buf + index); + index += msghdr->ifm_msglen; + + if (msghdr->ifm_type != RTM_IFINFO2) { + continue; + } + + if_msghdr2* msghdr2 = reinterpret_cast(msghdr); + sockaddr_dl* sockaddr = reinterpret_cast(msghdr2 + 1); + + // The interface name is not necessarily NUL-terminated + char name_buf[128]; + size_t name_len = MIN2(sizeof(name_buf) - 1, static_cast(sockaddr->sdl_nlen)); + strncpy(name_buf, sockaddr->sdl_data, name_len); + name_buf[name_len] = '\0'; + + uint64_t bytes_in = msghdr2->ifm_data.ifi_ibytes; + uint64_t bytes_out = msghdr2->ifm_data.ifi_obytes; + + NetworkInterface* cur = new NetworkInterface(name_buf, bytes_in, bytes_out, ret); + ret = cur; + } + + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/linux/os_perf_linux.cpp --- a/src/hotspot/os/linux/os_perf_linux.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/os/linux/os_perf_linux.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -44,6 +44,8 @@ #include #include #include +#include +#include /** /proc/[number]/stat @@ -1048,3 +1050,94 @@ cpu_info = *_cpu_info; // shallow copy assignment return OS_OK; } + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int64_t read_counter(const char* iface, const char* counter) const; + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { +} + +int64_t NetworkPerformanceInterface::NetworkPerformance::read_counter(const char* iface, const char* counter) const { + char buf[128]; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/statistics/%s", iface, counter); + + int fd = open(buf, O_RDONLY); + if (fd == -1) { + return -1; + } + + ssize_t num_bytes = read(fd, buf, sizeof(buf)); + close(fd); + if ((num_bytes == -1) || (num_bytes >= static_cast(sizeof(buf))) || (num_bytes < 1)) { + return -1; + } + + buf[num_bytes] = '\0'; + int64_t value = strtoll(buf, NULL, 10); + + return value; +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + ifaddrs* addresses; + ifaddrs* cur_address; + + if (getifaddrs(&addresses) != 0) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (cur_address = addresses; cur_address != NULL; cur_address = cur_address->ifa_next) { + if (cur_address->ifa_addr->sa_family != AF_PACKET) { + continue; + } + + int64_t bytes_in = read_counter(cur_address->ifa_name, "rx_bytes"); + int64_t bytes_out = read_counter(cur_address->ifa_name, "tx_bytes"); + + NetworkInterface* cur = new NetworkInterface(cur_address->ifa_name, bytes_in, bytes_out, ret); + ret = cur; + } + + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/solaris/os_perf_solaris.cpp --- a/src/hotspot/os/solaris/os_perf_solaris.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/os/solaris/os_perf_solaris.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -754,3 +754,88 @@ cpu_info = *_cpu_info; // shallow copy assignment return OS_OK; } + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() { + +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + return true; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { + +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const +{ + kstat_ctl_t* ctl = kstat_open(); + if (ctl == NULL) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (kstat_t* k = ctl->kc_chain; k != NULL; k = k->ks_next) { + if (strcmp(k->ks_class, "net") != 0) { + continue; + } + if (strcmp(k->ks_module, "link") != 0) { + continue; + } + + if (kstat_read(ctl, k, NULL) == -1) { + return OS_ERR; + } + + uint64_t bytes_in = UINT64_MAX; + uint64_t bytes_out = UINT64_MAX; + for (int i = 0; i < k->ks_ndata; ++i) { + kstat_named_t* data = &reinterpret_cast(k->ks_data)[i]; + if (strcmp(data->name, "rbytes64") == 0) { + bytes_in = data->value.ui64; + } + else if (strcmp(data->name, "obytes64") == 0) { + bytes_out = data->value.ui64; + } + } + + if ((bytes_in != UINT64_MAX) && (bytes_out != UINT64_MAX)) { + NetworkInterface* cur = new NetworkInterface(k->ks_name, bytes_in, bytes_out, ret); + ret = cur; + } + } + + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/windows/iphlp_interface.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/os/windows/iphlp_interface.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,105 @@ +/* + * 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" +#include "iphlp_interface.hpp" +#include "runtime/os.hpp" + +// IPHLP API +typedef DWORD(WINAPI *GetIfTable2_Fn)(PMIB_IF_TABLE2*); +typedef DWORD(WINAPI *FreeMibTable_Fn)(PVOID); + +// IPHLP statics +GetIfTable2_Fn IphlpDll::_GetIfTable2 = NULL; +FreeMibTable_Fn IphlpDll::_FreeMibTable = NULL; + +LONG IphlpDll::_critical_section = 0; +LONG IphlpDll::_initialized = 0; +LONG IphlpDll::_iphlp_reference_count = 0; +HMODULE IphlpDll::_hModule = NULL; + +void IphlpDll::initialize(void) { + _hModule = os::win32::load_Windows_dll("iphlpapi.dll", NULL, 0); + + if (NULL == _hModule) { + return; + } + + // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods + _GetIfTable2 = (GetIfTable2_Fn)::GetProcAddress(_hModule, "GetIfTable2"); + _FreeMibTable = (FreeMibTable_Fn)::GetProcAddress(_hModule, "FreeMibTable"); + + // interlock is used for fencing + InterlockedExchange(&_initialized, 1); +} + +bool IphlpDll::IphlpDetach(void) { + LONG prev_ref_count = InterlockedExchangeAdd(&_iphlp_reference_count, -1); + BOOL ret = false; + + if (1 == prev_ref_count) { + if (_initialized && _hModule != NULL) { + ret = FreeLibrary(_hModule); + if (ret) { + _hModule = NULL; + _GetIfTable2 = NULL; + _FreeMibTable = NULL; + InterlockedExchange(&_initialized, 0); + } + } + } + return ret != 0; +} + +bool IphlpDll::IphlpAttach(void) { + InterlockedExchangeAdd(&_iphlp_reference_count, 1); + + if (1 == _initialized) { + return true; + } + + while (InterlockedCompareExchange(&_critical_section, 1, 0) == 1); + + if (0 == _initialized) { + initialize(); + } + + while (InterlockedCompareExchange(&_critical_section, 0, 1) == 0); + + return (_GetIfTable2 != NULL && _FreeMibTable != NULL); +} + +DWORD IphlpDll::GetIfTable2(PMIB_IF_TABLE2* Table) { + assert(_initialized && _GetIfTable2 != NULL, + "IphlpAttach() not yet called"); + + return _GetIfTable2(Table); +} + +DWORD IphlpDll::FreeMibTable(PVOID Memory) { + assert(_initialized && _FreeMibTable != NULL, + "IphlpAttach() not yet called"); + + return _FreeMibTable(Memory); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/windows/iphlp_interface.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/os/windows/iphlp_interface.hpp Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,51 @@ +/* + * 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. + * + */ + +#ifndef OS_WINDOWS_VM_IPHLP_INTERFACE_HPP +#define OS_WINDOWS_VM_IPHLP_INTERFACE_HPP + +#include "memory/allocation.hpp" +#include "utilities/macros.hpp" +#include +#include +#include + +class IphlpDll : public AllStatic { + private: + static LONG _iphlp_reference_count; + static LONG _critical_section; + static LONG _initialized; + static HMODULE _hModule; + static void initialize(void); + static DWORD(WINAPI *_GetIfTable2)(PMIB_IF_TABLE2*); + static DWORD(WINAPI *_FreeMibTable)(PVOID); + + public: + static DWORD GetIfTable2(PMIB_IF_TABLE2*); + static DWORD FreeMibTable(PVOID); + static bool IphlpAttach(void); + static bool IphlpDetach(void); +}; + +#endif // OS_WINDOWS_VM_IPHLP_INTERFACE_HPP diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/os/windows/os_perf_windows.cpp --- a/src/hotspot/os/windows/os_perf_windows.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/os/windows/os_perf_windows.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -23,14 +23,15 @@ */ #include "precompiled.hpp" +#include "iphlp_interface.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "pdh_interface.hpp" #include "runtime/os_perf.hpp" #include "runtime/os.hpp" +#include "utilities/macros.hpp" #include "vm_version_ext_x86.hpp" -#include "utilities/macros.hpp" #include #include #include @@ -1380,3 +1381,78 @@ cpu_info = *_cpu_info; // shallow copy assignment return OS_OK; } + +class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj { + friend class NetworkPerformanceInterface; + private: + bool _iphlp_attached; + + NetworkPerformance(); + NetworkPerformance(const NetworkPerformance& rhs); // no impl + NetworkPerformance& operator=(const NetworkPerformance& rhs); // no impl + bool initialize(); + ~NetworkPerformance(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + +NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() +: _iphlp_attached(false) { +} + +bool NetworkPerformanceInterface::NetworkPerformance::initialize() { + _iphlp_attached = IphlpDll::IphlpAttach(); + return _iphlp_attached; +} + +NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() { + if (_iphlp_attached) { + IphlpDll::IphlpDetach(); + } +} + +int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const { + MIB_IF_TABLE2* table; + + if (IphlpDll::GetIfTable2(&table) != NO_ERROR) { + return OS_ERR; + } + + NetworkInterface* ret = NULL; + for (ULONG i = 0; i < table->NumEntries; ++i) { + if (table->Table[i].InterfaceAndOperStatusFlags.FilterInterface) { + continue; + } + + char buf[256]; + if (WideCharToMultiByte(CP_UTF8, 0, table->Table[i].Description, -1, buf, sizeof(buf), NULL, NULL) == 0) { + continue; + } + + NetworkInterface* cur = new NetworkInterface(buf, table->Table[i].InOctets, table->Table[i].OutOctets, ret); + ret = cur; + } + + IphlpDll::FreeMibTable(table); + *network_interfaces = ret; + + return OS_OK; +} + +NetworkPerformanceInterface::NetworkPerformanceInterface() { + _impl = NULL; +} + +NetworkPerformanceInterface::~NetworkPerformanceInterface() { + if (_impl != NULL) { + delete _impl; + } +} + +bool NetworkPerformanceInterface::initialize() { + _impl = new NetworkPerformanceInterface::NetworkPerformance(); + return _impl != NULL && _impl->initialize(); +} + +int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const { + return _impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/metadata/metadata.xml --- a/src/hotspot/share/jfr/metadata/metadata.xml Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/share/jfr/metadata/metadata.xml Thu Jun 28 15:06:55 2018 +0200 @@ -656,6 +656,12 @@ + + + + + + @@ -918,6 +924,10 @@ + + + + diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,191 @@ +/* + * 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" +#include "logging/log.hpp" +#include "jfr/jfrEvents.hpp" +#include "jfr/metadata/jfrSerializer.hpp" +#include "jfr/periodic/jfrNetworkUtilization.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" + +struct InterfaceEntry { + char* name; + traceid id; + uint64_t bytes_in; + uint64_t bytes_out; + bool in_use; +}; + +static GrowableArray* _interfaces = NULL; + +void JfrNetworkUtilization::destroy() { + if (_interfaces != NULL) { + for (int i = 0; i < _interfaces->length(); ++i) { + FREE_C_HEAP_ARRAY(char, _interfaces->at(i).name); + } + delete _interfaces; + _interfaces = NULL; + } +} + +static InterfaceEntry& new_entry(const NetworkInterface* iface, GrowableArray* interfaces) { + assert(iface != NULL, "invariant"); + assert(interfaces != NULL, "invariant"); + + // single threaded premise + static traceid interface_id = 0; + + const char* name = iface->get_name(); + assert(name != NULL, "invariant"); + + InterfaceEntry entry; + const size_t length = strlen(name); + entry.name = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal); + strncpy(entry.name, name, length + 1); + entry.id = ++interface_id; + entry.bytes_in = iface->get_bytes_in(); + entry.bytes_out = iface->get_bytes_out(); + entry.in_use = false; + return _interfaces->at(_interfaces->append(entry)); +} + +static GrowableArray* get_interfaces() { + if (_interfaces == NULL) { + _interfaces = new(ResourceObj::C_HEAP, mtTracing) GrowableArray(10, true, mtTracing); + } + return _interfaces; +} + +static InterfaceEntry& get_entry(const NetworkInterface* iface) { + // Remember the index we started at last time, since we're most likely looking at them + // in the same order every time. + static int saved_index = -1; + + GrowableArray* interfaces = get_interfaces(); + assert(interfaces != NULL, "invariant"); + for (int i = 0; i < _interfaces->length(); ++i) { + saved_index = (saved_index + 1) % _interfaces->length(); + if (strcmp(_interfaces->at(saved_index).name, iface->get_name()) == 0) { + return _interfaces->at(saved_index); + } + } + return new_entry(iface, interfaces); +} + +// If current counters are less than previous we assume the interface has been reset +// If no bytes have been either sent or received, we'll also skip the event +static uint64_t rate_per_second(uint64_t current, uint64_t old, const JfrTickspan& interval) { + assert(interval.value() > 0, "invariant"); + if (current <= old) { + return 0; + } + return ((current - old) * NANOSECS_PER_SEC) / interval.nanoseconds(); +} + +static bool get_interfaces(NetworkInterface** network_interfaces) { + const int ret_val = JfrOSInterface::network_utilization(network_interfaces); + if (ret_val == OS_ERR) { + log_debug(jfr, system)("Unable to generate network utilization events"); + return false; + } + return ret_val != FUNCTIONALITY_NOT_IMPLEMENTED; +} + +class JfrNetworkInterfaceName : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + assert(_interfaces != NULL, "invariant"); + const JfrCheckpointContext ctx = writer.context(); + const intptr_t count_offset = writer.reserve(sizeof(u4)); // Don't know how many yet + int active_interfaces = 0; + for (int i = 0; i < _interfaces->length(); ++i) { + InterfaceEntry& entry = _interfaces->at(i); + if (entry.in_use) { + entry.in_use = false; + writer.write_key(entry.id); + writer.write(entry.name); + ++active_interfaces; + } + } + if (active_interfaces == 0) { + // nothing to write, restore context + writer.set_context(ctx); + return; + } + writer.write_count(active_interfaces, count_offset); + } +}; + +static bool register_network_interface_name_serializer() { + assert(_interfaces != NULL, "invariant"); + return JfrSerializer::register_serializer(TYPE_NETWORKINTERFACENAME, + false, // require safepoint + false, // disallow caching; we want a callback every rotation + new JfrNetworkInterfaceName()); +} + +void JfrNetworkUtilization::send_events() { + ResourceMark rm; + NetworkInterface* network_interfaces; + if (!get_interfaces(&network_interfaces)) { + return; + } + log_trace(jfr, event)("Reporting network utilization"); + static JfrTicks last_sample_instant; + const JfrTicks cur_time = JfrTicks::now(); + const JfrTickspan interval = last_sample_instant == 0 ? cur_time - cur_time : cur_time - last_sample_instant; + last_sample_instant = cur_time; + for (NetworkInterface *cur = network_interfaces; cur != NULL; cur = cur->next()) { + InterfaceEntry& entry = get_entry(cur); + if (interval.value() > 0) { + const uint64_t current_bytes_in = cur->get_bytes_in(); + const uint64_t current_bytes_out = cur->get_bytes_out(); + const uint64_t read_rate = rate_per_second(current_bytes_in, entry.bytes_in, interval); + const uint64_t write_rate = rate_per_second(current_bytes_out, entry.bytes_out, interval); + if (read_rate > 0 || write_rate > 0) { + entry.in_use = true; + EventNetworkUtilization event(UNTIMED); + event.set_starttime(cur_time); + event.set_endtime(cur_time); + event.set_networkInterface(entry.id); + event.set_readRate(read_rate); + event.set_writeRate(write_rate); + event.commit(); + } + // update existing entry with new values + entry.bytes_in = current_bytes_in; + entry.bytes_out = current_bytes_out; + } + } + + static bool is_serializer_registered = false; + if (!is_serializer_registered) { + is_serializer_registered = register_network_interface_name_serializer(); + } +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/periodic/jfrNetworkUtilization.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.hpp Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,38 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP +#define SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP + +#include "memory/allocation.hpp" + +class NetworkInterface; + +class JfrNetworkUtilization : public AllStatic { +public: + static void destroy(); + static void send_events(); +}; + +#endif // SHARE_VM_JFR_PERIODIC_JFRNETWORKUTILIZATION_HPP diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/periodic/jfrOSInterface.cpp --- a/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/share/jfr/periodic/jfrOSInterface.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jfr/jfrEvents.hpp" +#include "jfr/periodic/jfrNetworkUtilization.hpp" #include "jfr/periodic/jfrOSInterface.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" @@ -54,6 +55,7 @@ } void JfrOSInterface::destroy() { + JfrNetworkUtilization::destroy(); if (_instance != NULL) { delete _instance; _instance = NULL; @@ -66,6 +68,7 @@ CPUInformationInterface* _cpu_info_interface; CPUPerformanceInterface* _cpu_perf_interface; SystemProcessInterface* _system_process_interface; + NetworkPerformanceInterface* _network_performance_interface; // stub helper void functionality_not_implemented(char** str) const; @@ -89,6 +92,8 @@ // system processes information int system_processes(SystemProcess** system_processes, int* no_of_sys_processes); + + int network_utilization(NetworkInterface** network_interfaces) const; }; JfrOSInterface::JfrOSInterfaceImpl::JfrOSInterfaceImpl() : _cpu_info_interface(NULL), @@ -97,18 +102,19 @@ bool JfrOSInterface::JfrOSInterfaceImpl::initialize() { _cpu_info_interface = new CPUInformationInterface(); - bool success = _cpu_info_interface != NULL && _cpu_info_interface->initialize(); - if (!success) { + if (!(_cpu_info_interface != NULL && _cpu_info_interface->initialize())) { return false; } _cpu_perf_interface = new CPUPerformanceInterface(); - success = _cpu_perf_interface != NULL && _cpu_perf_interface->initialize(); - if (!success) { + if (!(_cpu_perf_interface != NULL && _cpu_perf_interface->initialize())) { return false; } _system_process_interface = new SystemProcessInterface(); - success = _system_process_interface != NULL && _system_process_interface->initialize(); - return success; + if (!(_system_process_interface != NULL && _system_process_interface->initialize())) { + return false; + } + _network_performance_interface = new NetworkPerformanceInterface(); + return _network_performance_interface != NULL && _network_performance_interface->initialize(); } JfrOSInterface::JfrOSInterfaceImpl::~JfrOSInterfaceImpl(void) { @@ -124,6 +130,10 @@ delete _system_process_interface; _system_process_interface = NULL; } + if (_network_performance_interface != NULL) { + delete _network_performance_interface; + _network_performance_interface = NULL; + } } int JfrOSInterface::JfrOSInterfaceImpl::cpu_load(int which_logical_cpu, double* cpu_load) { @@ -154,6 +164,10 @@ return _system_process_interface->system_processes(system_processes, no_of_sys_processes); } +int JfrOSInterface::JfrOSInterfaceImpl::network_utilization(NetworkInterface** network_interfaces) const { + return _network_performance_interface->network_utilization(network_interfaces); +} + // assigned char* is RESOURCE_HEAP_ALLOCATED // caller need to ensure proper ResourceMark placement. int JfrOSInterface::JfrOSInterfaceImpl::os_version(char** os_version) const { @@ -246,3 +260,7 @@ int JfrOSInterface::system_processes(SystemProcess** sys_processes, int* no_of_sys_processes) { return instance()._impl->system_processes(sys_processes, no_of_sys_processes); } + +int JfrOSInterface::network_utilization(NetworkInterface** network_interfaces) { + return instance()._impl->network_utilization(network_interfaces); +} diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/periodic/jfrOSInterface.hpp --- a/src/hotspot/share/jfr/periodic/jfrOSInterface.hpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/share/jfr/periodic/jfrOSInterface.hpp Thu Jun 28 15:06:55 2018 +0200 @@ -26,10 +26,10 @@ #define SHARE_VM_JFR_PERIODIC_JFROSINTERFACE_HPP #include "jfr/utilities/jfrAllocation.hpp" -#include "utilities/globalDefinitions.hpp" class CPUInformation; class EnvironmentVariable; +class NetworkInterface; class SystemProcess; class JfrOSInterface: public JfrCHeapObj { @@ -54,6 +54,7 @@ static int os_version(char** os_version); static int generate_initial_environment_variable_events(); static int system_processes(SystemProcess** system_processes, int* no_of_sys_processes); + static int network_utilization(NetworkInterface** network_interfaces); }; #endif // SHARE_VM_JFR_PERIODIC_JFROSINTERFACE_HPP diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/jfr/periodic/jfrPeriodic.cpp --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -38,6 +38,7 @@ #include "jfr/periodic/jfrOSInterface.hpp" #include "jfr/periodic/jfrThreadCPULoadEvent.hpp" #include "jfr/periodic/jfrThreadDumpEvent.hpp" +#include "jfr/periodic/jfrNetworkUtilization.hpp" #include "jfr/recorder/jfrRecorder.hpp" #include "jfr/support/jfrThreadId.hpp" #include "jfr/utilities/jfrTime.hpp" @@ -176,6 +177,10 @@ JfrThreadCPULoadEvent::send_events(); } +TRACE_REQUEST_FUNC(NetworkUtilization) { + JfrNetworkUtilization::send_events(); +} + TRACE_REQUEST_FUNC(CPUTimeStampCounter) { EventCPUTimeStampCounter event; event.set_fastTimeEnabled(JfrTime::is_ft_enabled()); diff -r fb7800b66c92 -r d90c3cbf13df src/hotspot/share/runtime/os_perf.hpp --- a/src/hotspot/share/runtime/os_perf.hpp Thu Jun 28 18:04:19 2018 +0530 +++ b/src/hotspot/share/runtime/os_perf.hpp Thu Jun 28 15:06:55 2018 +0200 @@ -25,9 +25,8 @@ #ifndef SHARE_VM_RUNTIME_OS_PERF_HPP #define SHARE_VM_RUNTIME_OS_PERF_HPP +#include "memory/allocation.hpp" #include "utilities/macros.hpp" -#include "memory/allocation.hpp" -#include "utilities/globalDefinitions.hpp" #define FUNCTIONALITY_NOT_IMPLEMENTED -8 @@ -194,6 +193,47 @@ } }; +class NetworkInterface : public ResourceObj { + private: + char* _name; + uint64_t _bytes_in; + uint64_t _bytes_out; + NetworkInterface* _next; + + NetworkInterface(); // no impl + NetworkInterface(const NetworkInterface& rhs); // no impl + NetworkInterface& operator=(const NetworkInterface& rhs); // no impl + public: + NetworkInterface(const char* name, uint64_t bytes_in, uint64_t bytes_out, NetworkInterface* next) : + _name(NULL), + _bytes_in(bytes_in), + _bytes_out(bytes_out), + _next(next) { + assert(name != NULL, "invariant"); + const size_t length = strlen(name); + assert(allocated_on_res_area(), "invariant"); + _name = NEW_RESOURCE_ARRAY(char, length + 1); + strncpy(_name, name, length + 1); + assert(strncmp(_name, name, length) == 0, "invariant"); + } + + NetworkInterface* next() const { + return _next; + } + + const char* get_name() const { + return _name; + } + + uint64_t get_bytes_out() const { + return _bytes_out; + } + + uint64_t get_bytes_in() const { + return _bytes_in; + } +}; + class CPUInformationInterface : public CHeapObj { private: CPUInformation* _cpu_info; @@ -234,4 +274,17 @@ int system_processes(SystemProcess** system_procs, int* const no_of_sys_processes) const; }; +class NetworkPerformanceInterface : public CHeapObj { + private: + class NetworkPerformance; + NetworkPerformance* _impl; + NetworkPerformanceInterface(const NetworkPerformanceInterface& rhs); // no impl + NetworkPerformanceInterface& operator=(const NetworkPerformanceInterface& rhs); // no impl + public: + NetworkPerformanceInterface(); + bool initialize(); + ~NetworkPerformanceInterface(); + int network_utilization(NetworkInterface** network_interfaces) const; +}; + #endif // SHARE_VM_RUNTIME_OS_PERF_HPP diff -r fb7800b66c92 -r d90c3cbf13df src/jdk.jfr/share/conf/jfr/default.jfc --- a/src/jdk.jfr/share/conf/jfr/default.jfc Thu Jun 28 18:04:19 2018 +0530 +++ b/src/jdk.jfr/share/conf/jfr/default.jfc Thu Jun 28 15:06:55 2018 +0200 @@ -518,6 +518,11 @@ endChunk + + true + 5 s + + true beginChunk diff -r fb7800b66c92 -r d90c3cbf13df src/jdk.jfr/share/conf/jfr/profile.jfc --- a/src/jdk.jfr/share/conf/jfr/profile.jfc Thu Jun 28 18:04:19 2018 +0530 +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc Thu Jun 28 15:06:55 2018 +0200 @@ -518,6 +518,11 @@ endChunk + + true + 5 s + + true beginChunk diff -r fb7800b66c92 -r d90c3cbf13df test/hotspot/gtest/jfr/test_networkUtilization.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/gtest/jfr/test_networkUtilization.cpp Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,362 @@ +/* + * 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 +#include +#include + +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 JfrTicks; + typedef TimeInterval JfrTickspan; + + class MockJfrCheckpointWriter { + public: + traceid current; + std::map 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 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::const_iterator i = committed.begin(); + i != committed.end(); + ++i) { + if (name == i->iface) { + return *i; + } + } + return placeholder; + } + }; + + std::vector 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::next()); + } + }; + + class MockJfrOSInterface { + static std::list _interfaces; + + public: + MockJfrOSInterface() { + } + static int network_utilization(NetworkInterface** network_interfaces) { + *network_interfaces = NULL; + for (std::list::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 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()); +} diff -r fb7800b66c92 -r d90c3cbf13df test/jdk/jdk/jfr/event/runtime/TestNetworkUtilizationEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/jfr/event/runtime/TestNetworkUtilizationEvent.java Thu Jun 28 15:06:55 2018 +0200 @@ -0,0 +1,109 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.jfr.event.runtime; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.averagingLong; +import static java.util.stream.Collectors.groupingBy; + +/* + * @test + * @key jfr + * @library /test/lib + * + * @run main/othervm jdk.jfr.event.runtime.TestNetworkUtilizationEvent + */ +public class TestNetworkUtilizationEvent { + + private static final long packetSendCount = 100; + + public static void main(String[] args) throws Throwable { + testSimple(); + } + + static void testSimple() throws Throwable { + + Instant start = Instant.now(); + Recording recording = new Recording(); + recording.enable(EventNames.NetworkUtilization); + recording.start(); + + DatagramSocket socket = new DatagramSocket(); + String msg = "hello!"; + byte[] buf = msg.getBytes(); + + // Send a few packets both to the loopback address as well to an external + DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLoopbackAddress(), 12345); + for (int i = 0; i < packetSendCount; ++i) { + socket.send(packet); + } + packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.0.0.0"), 12345); + for (int i = 0; i < packetSendCount; ++i) { + socket.send(packet); + } + + // Now there should have been traffic on at least two different interfaces + recording.stop(); + Duration runtime = Duration.between(start, Instant.now()); + List events = Events.fromRecording(recording); + + // Calculate the average write rate for each interface + Map writeRates = events.stream() + .collect(groupingBy(e -> Events.assertField(e, "networkInterface").getValue(), + averagingLong(e -> Events.assertField(e, "writeRate").getValue()))); + + // Our test packets should have generated at least this much traffic per second + long expectedTraffic = (buf.length * packetSendCount) / Math.max(1, runtime.toSeconds()); + + // Count the number of interfaces that have seen at least our test traffic + long interfacesWithTraffic = writeRates.values().stream() + .filter(d -> d >= expectedTraffic) + .count(); + + if (Platform.isWindows() || Platform.isSolaris()) { + // Windows and Solaris do not track statistics for the loopback interface + Asserts.assertGreaterThanOrEqual(writeRates.size(), 1); + Asserts.assertGreaterThanOrEqual(interfacesWithTraffic, Long.valueOf(1)); + } else { + Asserts.assertGreaterThanOrEqual(writeRates.size(), 2); + Asserts.assertGreaterThanOrEqual(interfacesWithTraffic, Long.valueOf(2)); + } + } +} diff -r fb7800b66c92 -r d90c3cbf13df test/lib/jdk/test/lib/jfr/EventNames.java --- a/test/lib/jdk/test/lib/jfr/EventNames.java Thu Jun 28 18:04:19 2018 +0530 +++ b/test/lib/jdk/test/lib/jfr/EventNames.java Thu Jun 28 15:06:55 2018 +0200 @@ -155,6 +155,7 @@ public final static String InitialEnvironmentVariable = PREFIX + "InitialEnvironmentVariable"; public final static String NativeLibrary = PREFIX + "NativeLibrary"; public final static String PhysicalMemory = PREFIX + "PhysicalMemory"; + public final static String NetworkUtilization = PREFIX + "NetworkUtilization"; // JDK public static final String FileForce = PREFIX + "FileForce";