From 894edd0d6c939c9e0442dc2c2ee6cd5a740360f6 Mon Sep 17 00:00:00 2001 From: DC* Date: Mon, 5 Apr 2021 03:46:08 +0100 Subject: [PATCH 01/54] Add thread cpu to diagnostics --- .../clients/http/DiagnosticToadlet.java | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 269d5d069a4..4ea80439c93 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -2,11 +2,16 @@ import java.io.File; import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.Map; import freenet.client.HighLevelSimpleClient; @@ -409,15 +414,60 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { } textBuilder.append("\n"); - // drawThreadPriorityStatsBox - textBuilder.append("Threads:\n"); - int[] activeThreadsByPriority = stats.getActiveThreadsByPriority(); - int[] waitingThreadsByPriority = stats.getWaitingThreadsByPriority(); - for(int i=0; i threadCPU = new HashMap(); + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + threadMxBean.getThreadInfo(info.getThreadId()); + threadCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId())); + } + + RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + long initialUptime = runtimeMxBean.getUptime(); + + try {Thread.sleep(1000);} catch (InterruptedException e) {} + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + Long prev = threadCPU.get(info.getThreadId()); + if (prev == null) + continue; + threadCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId()) - prev); + } + + Float totalCPU = (runtimeMxBean.getUptime() - initialUptime) * 1000000F * 1; + + // ID, Name, Priority, Group (system, main), Status, % CPU + textBuilder.append( + String.format( + "%5s %-30s %5s %10s %-20s %-5s%n", + "ID", + "Name", + "Prio.", + "Group", + "Status", + "% CPU" + ) + ); + + for (Thread t : stats.getThreads()) { + if (t == null) + continue; + + Float cpuUsage = threadCPU.getOrDefault(t.getId(), 0l) / totalCPU; + String line = String.format( + "%5s %-30s %5s %10s %-20s %.2f%n", + t.getId(), + t.getName().length() > 30 ? t.getName().substring(0, 30) : t.getName(), + t.getPriority(), + t.getThreadGroup().getName(), + t.getState(), + cpuUsage + ); + + textBuilder.append(line); + } + textBuilder.append("\n"); } this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); From 64b256a57c900521a26795485e4182caba293ef2 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 10 Apr 2021 04:11:06 +0100 Subject: [PATCH 02/54] Add NodeDiagnostic module with NodeThreadDiagnostics --- .../clients/http/DiagnosticToadlet.java | 102 +++----- src/freenet/node/Node.java | 13 +- src/freenet/node/NodeDiagnostics.java | 219 ++++++++++++++++++ 3 files changed, 269 insertions(+), 65 deletions(-) create mode 100644 src/freenet/node/NodeDiagnostics.java diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 4ea80439c93..6d302e5f691 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -9,10 +9,7 @@ import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import freenet.client.HighLevelSimpleClient; import freenet.client.async.PersistenceDisabledException; @@ -25,15 +22,7 @@ import freenet.io.xfer.BlockReceiver; import freenet.io.xfer.BlockTransmitter; import freenet.l10n.BaseL10n; -import freenet.node.Node; -import freenet.node.NodeClientCore; -import freenet.node.NodeStarter; -import freenet.node.NodeStats; -import freenet.node.OpennetManager; -import freenet.node.PeerManager; -import freenet.node.PeerNodeStatus; -import freenet.node.RequestTracker; -import freenet.node.Version; +import freenet.node.*; import freenet.node.stats.DataStoreInstanceType; import freenet.node.stats.DataStoreStats; import freenet.node.stats.StatsNotAvailableException; @@ -414,60 +403,45 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { } textBuilder.append("\n"); - // drawThreadPriorityStatsBox - textBuilder.append("Threads:\n"); - ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - Map threadCPU = new HashMap(); - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - threadMxBean.getThreadInfo(info.getThreadId()); - threadCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId())); - } - - RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); - long initialUptime = runtimeMxBean.getUptime(); - - try {Thread.sleep(1000);} catch (InterruptedException e) {} - - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - Long prev = threadCPU.get(info.getThreadId()); - if (prev == null) - continue; - threadCPU.put(info.getThreadId(), threadMxBean.getThreadCpuTime(info.getThreadId()) - prev); + // drawThreadPriorityStatsBox + textBuilder.append("Threads:\n"); + // ID, Name, Priority, Group (system, main), Status, % CPU + textBuilder.append( + String.format( + "%5s %-60s %5s %10s %-20s %-5s%n", + "ID", + "Name", + "Prio.", + "Group", + "Status", + "% CPU" + ) + ); + + List threads = node.getNodeDiagnostics().getThreadInfo(); + threads.sort( + new Comparator() { + @Override + public int compare(NodeDiagnostics.NodeThreadInfo o1, NodeDiagnostics.NodeThreadInfo o2) { + return Double.compare(o2.getCpuTime(), o1.getCpuTime()); + } } - - Float totalCPU = (runtimeMxBean.getUptime() - initialUptime) * 1000000F * 1; - - // ID, Name, Priority, Group (system, main), Status, % CPU - textBuilder.append( - String.format( - "%5s %-30s %5s %10s %-20s %-5s%n", - "ID", - "Name", - "Prio.", - "Group", - "Status", - "% CPU" - ) + ); + + for (NodeDiagnostics.NodeThreadInfo t : threads) { + String line = String.format( + "%5s %-60s %5s %10s %-20s %.2f%n", + t.getId(), + t.getName().substring(0, Math.min(60, t.getName().length())), + t.getPrio(), + t.getGroupName(), + t.getState(), + t.getCpuTime() ); + textBuilder.append(line); + } - for (Thread t : stats.getThreads()) { - if (t == null) - continue; - - Float cpuUsage = threadCPU.getOrDefault(t.getId(), 0l) / totalCPU; - String line = String.format( - "%5s %-30s %5s %10s %-20s %.2f%n", - t.getId(), - t.getName().length() > 30 ? t.getName().substring(0, 30) : t.getName(), - t.getPriority(), - t.getThreadGroup().getName(), - t.getState(), - cpuUsage - ); - - textBuilder.append(line); - } - textBuilder.append("\n"); + textBuilder.append("\n"); } this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 6f19ae7c440..55eb7281d3c 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -723,6 +723,10 @@ public String[] getPossibleValues() { public final SecurityLevels securityLevels; + /** Diagnostics */ + public final NodeDiagnostics nodeDiagnostics; + + // Things that's needed to keep track of public final PluginManager pluginManager; @@ -2601,6 +2605,8 @@ public void realRun() { System.out.println("Node constructor completed"); new BandwidthManager(this).start(); + + nodeDiagnostics = new NodeDiagnostics(this.nodeStats, this.ticker); } private void peersOffersFrefFilesConfiguration(SubConfig nodeConfig, int configOptionSortOrder) { @@ -3166,6 +3172,8 @@ public void start(boolean noSwaps) throws NodeInitException { // Process any data in the extra peer data directory peers.readExtraPeerData(); + nodeDiagnostics.start(); + Logger.normal(this, "Started node"); hasStarted = true; @@ -4892,5 +4900,8 @@ public PluginManager getPluginManager() { DatabaseKey getDatabaseKey() { return databaseKey; } - + + public NodeDiagnostics getNodeDiagnostics() { + return nodeDiagnostics; + } } diff --git a/src/freenet/node/NodeDiagnostics.java b/src/freenet/node/NodeDiagnostics.java new file mode 100644 index 00000000000..486de210c85 --- /dev/null +++ b/src/freenet/node/NodeDiagnostics.java @@ -0,0 +1,219 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.node; + +import freenet.support.Ticker; + +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; +import java.lang.management.ThreadInfo; +import java.util.List; +import java.util.Collections; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Map; + +/** + * @author desyncr + * + * A class to retrieve data to build diagnostic dumps to help in determining + * node bottlenecks or misconfiguration. + * + * This class launches various threads at intervals to retrieve information. This information + * is available through the public methods. + * Some data pointers are obtained from NodeStats object. + */ +public class NodeDiagnostics { + private final NodeStats nodeStats; + private final Ticker ticker; + private final ThreadMonitor threadMonitor; + + /** Interval in ms the ThreadMonitor is launched */ + private static final int MONITOR_INTERVAL = 10000; + private static final String MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; + + /** + * @param nodeStats Used to retrieve data points. + * @param ticker Used to queue timed jobs. + */ + NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { + this.nodeStats = nodeStats; + this.ticker = ticker; + this.threadMonitor = new ThreadMonitor(MONITOR_THREAD_NAME, MONITOR_INTERVAL); + } + + public void start() throws NodeInitException { + threadMonitor.start(); + } + + /** + * + * @return List of threads registered in NodeStats.getThreads() + */ + public List getThreadInfo() { + return threadMonitor.getThreads(); + } + + /** + * Runnable thread to retrieve node thread's information and compiling it into + * an array of NodeThreadInfo objects. + */ + private class ThreadMonitor implements Runnable { + private final String name; + private final int monitorInterval; + + /** Sleep interval to calculate % CPU used by each thread */ + private static final int CPU_SLEEP_INTERVAL = 1000; + List nodeThreadInfos = Collections.synchronizedList(new ArrayList<>()); + + OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + + ThreadMonitor(String name, int monitorInterval) { + this.name = name; + this.monitorInterval = monitorInterval; + } + + public void start() { + scheduleNext(0); + } + + private void scheduleNext(int interval) { + ticker.queueTimedJob( + this, + name, + interval, + false, + true + ); + } + + private void scheduleNext() { + scheduleNext(monitorInterval); + } + + @Override + public void run() { + Map threadCPU = new HashMap<>(); + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + threadCPU.put( + info.getThreadId(), + threadMxBean.getThreadCpuTime( + info.getThreadId() + ) + ); + } + + long initialUptime = runtimeMxBean.getUptime(); + + try { + Thread.sleep(CPU_SLEEP_INTERVAL); + } catch (InterruptedException ignored) { + } + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + Long prev = threadCPU.get(info.getThreadId()); + if (prev == null) { + continue; + } + + threadCPU.put( + info.getThreadId(), + threadMxBean.getThreadCpuTime(info.getThreadId()) - prev + ); + } + + long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; + double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000F; + + List threads = new ArrayList<>(); + for (Thread t : nodeStats.getThreads()) { + if (t == null) { + continue; + } + + double cpuUsage = threadCPU.getOrDefault(t.getId(), 0L) / totalElapsedUptime; + NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( + t.getId(), + t.getName(), + t.getPriority(), + t.getThreadGroup().getName(), + t.getState().toString(), + cpuUsage + ); + + threads.add(nodeThreadInfo); + } + synchronized (this) { + nodeThreadInfos = threads; + } + + scheduleNext(); + } + + /** + * @return List of Node threads + */ + public synchronized List getThreads() { + return new ArrayList<>(nodeThreadInfos); + } + } + + /** + * Class to wrap node thread information. + */ + public class NodeThreadInfo { + private final long id; + private final String name; + private final int prio; + private final String groupName; + private final String state; + private final double cpu_time; + + /** + * @param id Thread ID + * @param name Thread name, or + * @param prio Thread priority + * @param groupName Thread's group name + * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) + * @param cpu_time Thread's % of CPU time used + */ + NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpu_time) { + this.id = id; + this.name = name; + this.prio = prio; + this.groupName = groupName; + this.state = state; + this.cpu_time = cpu_time; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPrio() { + return prio; + } + + public String getGroupName() { + return groupName; + } + + public String getState() { + return state; + } + + public double getCpuTime() { + return cpu_time; + } + } +} From 299c2dfcc80a7d5259a16bc4b31faab59ea41828 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 10 Apr 2021 04:22:07 +0100 Subject: [PATCH 03/54] Don't use single class imports --- src/freenet/clients/http/DiagnosticToadlet.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 6d302e5f691..ca15c5bc943 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -2,15 +2,13 @@ import java.io.File; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.*; - +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.List; import freenet.client.HighLevelSimpleClient; import freenet.client.async.PersistenceDisabledException; import freenet.clients.fcp.DownloadRequestStatus; From 5b7b4860fdd947642f5bdd7d627637bcd2621e51 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 10 Apr 2021 04:24:06 +0100 Subject: [PATCH 04/54] Fix indentation --- src/freenet/node/Node.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 55eb7281d3c..4515e3b3c43 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -4902,6 +4902,6 @@ DatabaseKey getDatabaseKey() { } public NodeDiagnostics getNodeDiagnostics() { - return nodeDiagnostics; + return nodeDiagnostics; } } From cafa4a5315615dce91baff17ef126adce4c1145e Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 10 Apr 2021 04:29:40 +0100 Subject: [PATCH 05/54] Don't use single class imports --- src/freenet/clients/http/DiagnosticToadlet.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index ca15c5bc943..3cb9341481e 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -20,7 +20,16 @@ import freenet.io.xfer.BlockReceiver; import freenet.io.xfer.BlockTransmitter; import freenet.l10n.BaseL10n; -import freenet.node.*; +import freenet.node.Node; +import freenet.node.NodeClientCore; +import freenet.node.NodeDiagnostics; +import freenet.node.NodeStarter; +import freenet.node.NodeStats; +import freenet.node.OpennetManager; +import freenet.node.PeerManager; +import freenet.node.PeerNodeStatus; +import freenet.node.RequestTracker; +import freenet.node.Version; import freenet.node.stats.DataStoreInstanceType; import freenet.node.stats.DataStoreStats; import freenet.node.stats.StatsNotAvailableException; From c999a37cec975f6ed9257afa3809129d70ecacbd Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 11 Apr 2021 01:58:51 +0100 Subject: [PATCH 06/54] Create Diagnostics component to be able to scale to support multiple diagnostics easily --- .../clients/http/DiagnosticToadlet.java | 19 +- src/freenet/node/Node.java | 1 + src/freenet/node/NodeDiagnostics.java | 219 ------------------ .../node/diagnostics/NodeDiagnostics.java | 53 +++++ .../diagnostics/diagnostics/Diagnostics.java | 5 + .../diagnostics/threads/NodeThreadInfo.java | 54 +++++ .../threads/ThreadsDiagnostics.java | 138 +++++++++++ 7 files changed, 261 insertions(+), 228 deletions(-) delete mode 100644 src/freenet/node/NodeDiagnostics.java create mode 100644 src/freenet/node/diagnostics/NodeDiagnostics.java create mode 100644 src/freenet/node/diagnostics/diagnostics/Diagnostics.java create mode 100644 src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java create mode 100644 src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 3cb9341481e..50ce8f68b60 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -20,9 +20,9 @@ import freenet.io.xfer.BlockReceiver; import freenet.io.xfer.BlockTransmitter; import freenet.l10n.BaseL10n; +import freenet.node.diagnostics.NodeDiagnostics; import freenet.node.Node; import freenet.node.NodeClientCore; -import freenet.node.NodeDiagnostics; import freenet.node.NodeStarter; import freenet.node.NodeStats; import freenet.node.OpennetManager; @@ -30,6 +30,8 @@ import freenet.node.PeerNodeStatus; import freenet.node.RequestTracker; import freenet.node.Version; +import freenet.node.diagnostics.diagnostics.*; +import freenet.node.diagnostics.diagnostics.threads.*; import freenet.node.stats.DataStoreInstanceType; import freenet.node.stats.DataStoreStats; import freenet.node.stats.StatsNotAvailableException; @@ -425,17 +427,16 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { ) ); - List threads = node.getNodeDiagnostics().getThreadInfo(); + ThreadsDiagnostics threadsDiagnostics = (ThreadsDiagnostics) node + .getNodeDiagnostics() + .getDiagnostics(NodeDiagnostics.DIAGNOSTICS.THREADS); + + List threads = threadsDiagnostics.getThreads(); threads.sort( - new Comparator() { - @Override - public int compare(NodeDiagnostics.NodeThreadInfo o1, NodeDiagnostics.NodeThreadInfo o2) { - return Double.compare(o2.getCpuTime(), o1.getCpuTime()); - } - } + (o1, o2) -> Double.compare(o2.getCpuTime(), o1.getCpuTime()) ); - for (NodeDiagnostics.NodeThreadInfo t : threads) { + for (NodeThreadInfo t : threads) { String line = String.format( "%5s %-60s %5s %10s %-20s %.2f%n", t.getId(), diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 4515e3b3c43..62110539c1b 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -82,6 +82,7 @@ import freenet.keys.SSKVerifyException; import freenet.l10n.BaseL10n; import freenet.l10n.NodeL10n; +import freenet.node.diagnostics.NodeDiagnostics; import freenet.node.DarknetPeerNode.FRIEND_TRUST; import freenet.node.DarknetPeerNode.FRIEND_VISIBILITY; import freenet.node.NodeDispatcher.NodeDispatcherCallback; diff --git a/src/freenet/node/NodeDiagnostics.java b/src/freenet/node/NodeDiagnostics.java deleted file mode 100644 index 486de210c85..00000000000 --- a/src/freenet/node/NodeDiagnostics.java +++ /dev/null @@ -1,219 +0,0 @@ -/* This code is part of Freenet. It is distributed under the GNU General - * Public License, version 2 (or at your option any later version). See - * http://www.gnu.org/ for further details of the GPL. */ -package freenet.node; - -import freenet.support.Ticker; - -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; -import java.lang.management.RuntimeMXBean; -import java.lang.management.ThreadMXBean; -import java.lang.management.ThreadInfo; -import java.util.List; -import java.util.Collections; -import java.util.HashMap; -import java.util.ArrayList; -import java.util.Map; - -/** - * @author desyncr - * - * A class to retrieve data to build diagnostic dumps to help in determining - * node bottlenecks or misconfiguration. - * - * This class launches various threads at intervals to retrieve information. This information - * is available through the public methods. - * Some data pointers are obtained from NodeStats object. - */ -public class NodeDiagnostics { - private final NodeStats nodeStats; - private final Ticker ticker; - private final ThreadMonitor threadMonitor; - - /** Interval in ms the ThreadMonitor is launched */ - private static final int MONITOR_INTERVAL = 10000; - private static final String MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; - - /** - * @param nodeStats Used to retrieve data points. - * @param ticker Used to queue timed jobs. - */ - NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { - this.nodeStats = nodeStats; - this.ticker = ticker; - this.threadMonitor = new ThreadMonitor(MONITOR_THREAD_NAME, MONITOR_INTERVAL); - } - - public void start() throws NodeInitException { - threadMonitor.start(); - } - - /** - * - * @return List of threads registered in NodeStats.getThreads() - */ - public List getThreadInfo() { - return threadMonitor.getThreads(); - } - - /** - * Runnable thread to retrieve node thread's information and compiling it into - * an array of NodeThreadInfo objects. - */ - private class ThreadMonitor implements Runnable { - private final String name; - private final int monitorInterval; - - /** Sleep interval to calculate % CPU used by each thread */ - private static final int CPU_SLEEP_INTERVAL = 1000; - List nodeThreadInfos = Collections.synchronizedList(new ArrayList<>()); - - OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); - ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); - - ThreadMonitor(String name, int monitorInterval) { - this.name = name; - this.monitorInterval = monitorInterval; - } - - public void start() { - scheduleNext(0); - } - - private void scheduleNext(int interval) { - ticker.queueTimedJob( - this, - name, - interval, - false, - true - ); - } - - private void scheduleNext() { - scheduleNext(monitorInterval); - } - - @Override - public void run() { - Map threadCPU = new HashMap<>(); - - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - threadCPU.put( - info.getThreadId(), - threadMxBean.getThreadCpuTime( - info.getThreadId() - ) - ); - } - - long initialUptime = runtimeMxBean.getUptime(); - - try { - Thread.sleep(CPU_SLEEP_INTERVAL); - } catch (InterruptedException ignored) { - } - - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - Long prev = threadCPU.get(info.getThreadId()); - if (prev == null) { - continue; - } - - threadCPU.put( - info.getThreadId(), - threadMxBean.getThreadCpuTime(info.getThreadId()) - prev - ); - } - - long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; - double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000F; - - List threads = new ArrayList<>(); - for (Thread t : nodeStats.getThreads()) { - if (t == null) { - continue; - } - - double cpuUsage = threadCPU.getOrDefault(t.getId(), 0L) / totalElapsedUptime; - NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( - t.getId(), - t.getName(), - t.getPriority(), - t.getThreadGroup().getName(), - t.getState().toString(), - cpuUsage - ); - - threads.add(nodeThreadInfo); - } - synchronized (this) { - nodeThreadInfos = threads; - } - - scheduleNext(); - } - - /** - * @return List of Node threads - */ - public synchronized List getThreads() { - return new ArrayList<>(nodeThreadInfos); - } - } - - /** - * Class to wrap node thread information. - */ - public class NodeThreadInfo { - private final long id; - private final String name; - private final int prio; - private final String groupName; - private final String state; - private final double cpu_time; - - /** - * @param id Thread ID - * @param name Thread name, or - * @param prio Thread priority - * @param groupName Thread's group name - * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) - * @param cpu_time Thread's % of CPU time used - */ - NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpu_time) { - this.id = id; - this.name = name; - this.prio = prio; - this.groupName = groupName; - this.state = state; - this.cpu_time = cpu_time; - } - - public long getId() { - return id; - } - - public String getName() { - return name; - } - - public int getPrio() { - return prio; - } - - public String getGroupName() { - return groupName; - } - - public String getState() { - return state; - } - - public double getCpuTime() { - return cpu_time; - } - } -} diff --git a/src/freenet/node/diagnostics/NodeDiagnostics.java b/src/freenet/node/diagnostics/NodeDiagnostics.java new file mode 100644 index 00000000000..c033a31f4b7 --- /dev/null +++ b/src/freenet/node/diagnostics/NodeDiagnostics.java @@ -0,0 +1,53 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.node.diagnostics; + +import freenet.node.diagnostics.diagnostics.*; +import freenet.node.diagnostics.diagnostics.threads.*; +import freenet.support.Ticker; +import freenet.node.NodeStats; +import freenet.node.NodeInitException; + +import java.util.HashMap; + +/** + * @author desyncr + * + * A class to retrieve data to build diagnostic dumps to help in determining + * node bottlenecks or misconfiguration. + * + * This class launches various threads at intervals to retrieve information. This information + * is available through the public methods. + * Some data pointers are obtained from NodeStats object. + */ +public class NodeDiagnostics { + public enum DIAGNOSTICS { + THREADS + }; + + private final HashMap diagnostics = new HashMap<>(); + + /** + * @param nodeStats Used to retrieve data points. + * @param ticker Used to queue timed jobs. + */ + public NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { + this.diagnostics.put( + DIAGNOSTICS.THREADS, + new ThreadsDiagnostics(nodeStats, ticker) + ); + } + + public void start() throws NodeInitException { + diagnostics.forEach((type, diagnostic) -> diagnostic.start()); + } + + /** + * + * @return List of threads registered in NodeStats.getThreads() + */ + public Diagnostics getDiagnostics(DIAGNOSTICS diagnostic) { + return diagnostics.get(diagnostic); + } +} diff --git a/src/freenet/node/diagnostics/diagnostics/Diagnostics.java b/src/freenet/node/diagnostics/diagnostics/Diagnostics.java new file mode 100644 index 00000000000..c2ab30ef9c0 --- /dev/null +++ b/src/freenet/node/diagnostics/diagnostics/Diagnostics.java @@ -0,0 +1,5 @@ +package freenet.node.diagnostics.diagnostics; + +public interface Diagnostics { + void start(); +} diff --git a/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java new file mode 100644 index 00000000000..21886836bbe --- /dev/null +++ b/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java @@ -0,0 +1,54 @@ +package freenet.node.diagnostics.diagnostics.threads; + +/** + * Class to wrap node thread information. + */ +public class NodeThreadInfo { + private final long id; + private final String name; + private final int prio; + private final String groupName; + private final String state; + private final double cpu_time; + + /** + * @param id Thread ID + * @param name Thread name, or + * @param prio Thread priority + * @param groupName Thread's group name + * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) + * @param cpu_time Thread's % of CPU time used + */ + NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpu_time) { + this.id = id; + this.name = name; + this.prio = prio; + this.groupName = groupName; + this.state = state; + this.cpu_time = cpu_time; + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPrio() { + return prio; + } + + public String getGroupName() { + return groupName; + } + + public String getState() { + return state; + } + + public double getCpuTime() { + return cpu_time; + } +} \ No newline at end of file diff --git a/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java b/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java new file mode 100644 index 00000000000..c20ad1be5ef --- /dev/null +++ b/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java @@ -0,0 +1,138 @@ +package freenet.node.diagnostics.diagnostics.threads; + +import freenet.node.*; +import freenet.node.diagnostics.diagnostics.*; +import freenet.support.*; + +import java.lang.management.*; +import java.util.*; + +/** + * Runnable thread to retrieve node thread's information and compiling it into + * an array of NodeThreadInfo objects. + */ +public class ThreadsDiagnostics implements Runnable, Diagnostics { + private final String name; + private final int monitorInterval; + + private final NodeStats nodeStats; + private final Ticker ticker; + + /** Interval in ms the ThreadMonitor is launched */ + private static final int MONITOR_INTERVAL = 10000; + private static final String MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; + + /** Sleep interval to calculate % CPU used by each thread */ + private static final int CPU_SLEEP_INTERVAL = 1000; + List nodeThreadInfos = Collections.synchronizedList(new ArrayList<>()); + + OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + + /** + * @param nodeStats Used to retrieve data points + * @param ticker Used to queue timed jobs + * @param name Thread name + * @param monitorInterval Sleep intervals to retrieve CPU usage + */ + public ThreadsDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int monitorInterval) { + this.nodeStats = nodeStats; + this.ticker = ticker; + this.name = name; + this.monitorInterval = monitorInterval; + } + + /* + * @param nodeStats Used to retrieve data points + * @param ticker Used to queue timed jobs + */ + public ThreadsDiagnostics(NodeStats nodeStats, Ticker ticker) { + this(nodeStats, ticker, MONITOR_THREAD_NAME, MONITOR_INTERVAL); + } + + private void scheduleNext(int interval) { + ticker.queueTimedJob( + this, + name, + interval, + false, + true + ); + } + + public void start() { + scheduleNext(0); + } + + private void scheduleNext() { + scheduleNext(monitorInterval); + } + + @Override + public void run() { + Map threadCPU = new HashMap<>(); + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + threadCPU.put( + info.getThreadId(), + threadMxBean.getThreadCpuTime( + info.getThreadId() + ) + ); + } + + long initialUptime = runtimeMxBean.getUptime(); + + try { + Thread.sleep(CPU_SLEEP_INTERVAL); + } catch (InterruptedException ignored) { + } + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + Long prev = threadCPU.get(info.getThreadId()); + if (prev == null) { + continue; + } + + threadCPU.put( + info.getThreadId(), + threadMxBean.getThreadCpuTime(info.getThreadId()) - prev + ); + } + + long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; + double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000F; + + List threads = new ArrayList<>(); + for (Thread t : nodeStats.getThreads()) { + if (t == null) { + continue; + } + + double cpuUsage = threadCPU.getOrDefault(t.getId(), 0L) / totalElapsedUptime; + NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( + t.getId(), + t.getName(), + t.getPriority(), + t.getThreadGroup().getName(), + t.getState().toString(), + cpuUsage + ); + + threads.add(nodeThreadInfo); + } + synchronized (this) { + nodeThreadInfos = threads; + } + + scheduleNext(); + } + + /** + * @return List of Node threads + */ + public synchronized List getThreads() { + return new ArrayList<>(nodeThreadInfos); + } +} From 0bf9c259d3c2bd6fed8c7dfa294ddee17b5d5bdd Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 11 Apr 2021 02:01:25 +0100 Subject: [PATCH 07/54] Add license headers to new files --- src/freenet/node/diagnostics/diagnostics/Diagnostics.java | 3 +++ .../node/diagnostics/diagnostics/threads/NodeThreadInfo.java | 3 +++ .../diagnostics/diagnostics/threads/ThreadsDiagnostics.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/freenet/node/diagnostics/diagnostics/Diagnostics.java b/src/freenet/node/diagnostics/diagnostics/Diagnostics.java index c2ab30ef9c0..f9a19bb97cb 100644 --- a/src/freenet/node/diagnostics/diagnostics/Diagnostics.java +++ b/src/freenet/node/diagnostics/diagnostics/Diagnostics.java @@ -1,3 +1,6 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics.diagnostics; public interface Diagnostics { diff --git a/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java index 21886836bbe..f5666ce5a65 100644 --- a/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java @@ -1,3 +1,6 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics.diagnostics.threads; /** diff --git a/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java b/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java index c20ad1be5ef..3d77e446398 100644 --- a/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java +++ b/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java @@ -1,3 +1,6 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics.diagnostics.threads; import freenet.node.*; From d1c75ad1eddd097684c974f7d619401845f86c2f Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 00:32:39 +0100 Subject: [PATCH 08/54] Move thread info building into its own method --- .../clients/http/DiagnosticToadlet.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 50ce8f68b60..f1a5172d1cd 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -414,8 +414,18 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { // drawThreadPriorityStatsBox textBuilder.append("Threads:\n"); + textBuilder.append(threadsStats()); + + textBuilder.append("\n"); + } + + this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); + } + + private StringBuilder threadsStats() { + StringBuilder sb = new StringBuilder(); // ID, Name, Priority, Group (system, main), Status, % CPU - textBuilder.append( + sb.append( String.format( "%5s %-60s %5s %10s %-20s %-5s%n", "ID", @@ -436,23 +446,20 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { (o1, o2) -> Double.compare(o2.getCpuTime(), o1.getCpuTime()) ); - for (NodeThreadInfo t : threads) { + for (NodeThreadInfo thread : threads) { String line = String.format( "%5s %-60s %5s %10s %-20s %.2f%n", - t.getId(), - t.getName().substring(0, Math.min(60, t.getName().length())), - t.getPrio(), - t.getGroupName(), - t.getState(), - t.getCpuTime() + thread.getId(), + thread.getName().substring(0, Math.min(60, thread.getName().length())), + thread.getPrio(), + thread.getGroupName(), + thread.getState(), + thread.getCpuTime() ); - textBuilder.append(line); + sb.append(line); } - textBuilder.append("\n"); - } - - this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); + return sb; } private int getPeerStatusCount(PeerNodeStatus[] peerNodeStatuses, int status) { From 3c58658169dbf47ade991e15cdf6c51977ae77a2 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 00:34:23 +0100 Subject: [PATCH 09/54] Make NodeDiagnostics field private --- src/freenet/node/Node.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 62110539c1b..feb94fc30b9 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -725,8 +725,7 @@ public String[] getPossibleValues() { public final SecurityLevels securityLevels; /** Diagnostics */ - public final NodeDiagnostics nodeDiagnostics; - + private final NodeDiagnostics nodeDiagnostics; // Things that's needed to keep track of public final PluginManager pluginManager; From 23d6e38eeb179dd7996e05201182b1fa6408c523 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 00:39:08 +0100 Subject: [PATCH 10/54] Flatten NodeDiagnostics interface --- src/freenet/clients/http/DiagnosticToadlet.java | 4 ++-- src/freenet/node/diagnostics/NodeDiagnostics.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index f1a5172d1cd..af9467e0b4b 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -437,9 +437,9 @@ private StringBuilder threadsStats() { ) ); - ThreadsDiagnostics threadsDiagnostics = (ThreadsDiagnostics) node + ThreadsDiagnostics threadsDiagnostics = node .getNodeDiagnostics() - .getDiagnostics(NodeDiagnostics.DIAGNOSTICS.THREADS); + .getThreadsDiagnostics(); List threads = threadsDiagnostics.getThreads(); threads.sort( diff --git a/src/freenet/node/diagnostics/NodeDiagnostics.java b/src/freenet/node/diagnostics/NodeDiagnostics.java index c033a31f4b7..caaf6daccc8 100644 --- a/src/freenet/node/diagnostics/NodeDiagnostics.java +++ b/src/freenet/node/diagnostics/NodeDiagnostics.java @@ -22,7 +22,7 @@ * Some data pointers are obtained from NodeStats object. */ public class NodeDiagnostics { - public enum DIAGNOSTICS { + private enum DIAGNOSTICS { THREADS }; @@ -47,7 +47,7 @@ public void start() throws NodeInitException { * * @return List of threads registered in NodeStats.getThreads() */ - public Diagnostics getDiagnostics(DIAGNOSTICS diagnostic) { - return diagnostics.get(diagnostic); + public ThreadsDiagnostics getThreadsDiagnostics() { + return (ThreadsDiagnostics)diagnostics.get(DIAGNOSTICS.THREADS); } } From de33536b8ca94789a62ed064818f1547c39c660a Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:06:16 +0100 Subject: [PATCH 11/54] Flatten NodeDiagnostics interface --- .../diagnostics/DefaultNodeDiagnostics.java | 52 +++++++++++++++++++ .../node/diagnostics/NodeDiagnostics.java | 49 +---------------- ...iagnostics.java => ThreadDiagnostics.java} | 2 +- .../DefaultThreadDiagnostics.java} | 10 ++-- .../threads/NodeThreadInfo.java | 0 5 files changed, 60 insertions(+), 53 deletions(-) create mode 100644 src/freenet/node/diagnostics/DefaultNodeDiagnostics.java rename src/freenet/node/diagnostics/{diagnostics/Diagnostics.java => ThreadDiagnostics.java} (84%) rename src/freenet/node/diagnostics/{diagnostics/threads/ThreadsDiagnostics.java => threads/DefaultThreadDiagnostics.java} (92%) rename src/freenet/node/diagnostics/{diagnostics => }/threads/NodeThreadInfo.java (100%) diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java new file mode 100644 index 00000000000..a16d69014f2 --- /dev/null +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -0,0 +1,52 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.node.diagnostics; + +import freenet.node.diagnostics.threads.*; +import freenet.support.Ticker; +import freenet.node.NodeStats; +import freenet.node.NodeInitException; + +import java.util.HashMap; + +/** + * @author desyncr + * + * A class to retrieve data to build diagnostic dumps to help in determining + * node bottlenecks or misconfiguration. + * + * This class launches various threads at intervals to retrieve information. This information + * is available through the public methods. + * Some data pointers are obtained from NodeStats object. + */ +public class NodeDiagnostics { + private enum DIAGNOSTICS { + THREADS + }; + + private final HashMap diagnostics = new HashMap<>(); + + /** + * @param nodeStats Used to retrieve data points. + * @param ticker Used to queue timed jobs. + */ + public NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { + this.diagnostics.put( + DIAGNOSTICS.THREADS, + new ThreadsDiagnostics(nodeStats, ticker) + ); + } + + public void start() throws NodeInitException { + diagnostics.forEach((type, diagnostic) -> diagnostic.start()); + } + + /** + * + * @return List of threads registered in NodeStats.getThreads() + */ + public ThreadsDiagnostics getThreadsDiagnostics() { + return (ThreadsDiagnostics)diagnostics.get(DIAGNOSTICS.THREADS); + } +} diff --git a/src/freenet/node/diagnostics/NodeDiagnostics.java b/src/freenet/node/diagnostics/NodeDiagnostics.java index caaf6daccc8..e27121accd1 100644 --- a/src/freenet/node/diagnostics/NodeDiagnostics.java +++ b/src/freenet/node/diagnostics/NodeDiagnostics.java @@ -3,51 +3,6 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics; -import freenet.node.diagnostics.diagnostics.*; -import freenet.node.diagnostics.diagnostics.threads.*; -import freenet.support.Ticker; -import freenet.node.NodeStats; -import freenet.node.NodeInitException; - -import java.util.HashMap; - -/** - * @author desyncr - * - * A class to retrieve data to build diagnostic dumps to help in determining - * node bottlenecks or misconfiguration. - * - * This class launches various threads at intervals to retrieve information. This information - * is available through the public methods. - * Some data pointers are obtained from NodeStats object. - */ -public class NodeDiagnostics { - private enum DIAGNOSTICS { - THREADS - }; - - private final HashMap diagnostics = new HashMap<>(); - - /** - * @param nodeStats Used to retrieve data points. - * @param ticker Used to queue timed jobs. - */ - public NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { - this.diagnostics.put( - DIAGNOSTICS.THREADS, - new ThreadsDiagnostics(nodeStats, ticker) - ); - } - - public void start() throws NodeInitException { - diagnostics.forEach((type, diagnostic) -> diagnostic.start()); - } - - /** - * - * @return List of threads registered in NodeStats.getThreads() - */ - public ThreadsDiagnostics getThreadsDiagnostics() { - return (ThreadsDiagnostics)diagnostics.get(DIAGNOSTICS.THREADS); - } +public interface Diagnostics { + void start(); } diff --git a/src/freenet/node/diagnostics/diagnostics/Diagnostics.java b/src/freenet/node/diagnostics/ThreadDiagnostics.java similarity index 84% rename from src/freenet/node/diagnostics/diagnostics/Diagnostics.java rename to src/freenet/node/diagnostics/ThreadDiagnostics.java index f9a19bb97cb..e27121accd1 100644 --- a/src/freenet/node/diagnostics/diagnostics/Diagnostics.java +++ b/src/freenet/node/diagnostics/ThreadDiagnostics.java @@ -1,7 +1,7 @@ /* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ -package freenet.node.diagnostics.diagnostics; +package freenet.node.diagnostics; public interface Diagnostics { void start(); diff --git a/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java similarity index 92% rename from src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java rename to src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 3d77e446398..9f1e8936174 100644 --- a/src/freenet/node/diagnostics/diagnostics/threads/ThreadsDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -1,10 +1,10 @@ /* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ -package freenet.node.diagnostics.diagnostics.threads; +package freenet.node.diagnostics.threads; import freenet.node.*; -import freenet.node.diagnostics.diagnostics.*; +import freenet.node.diagnostics.*; import freenet.support.*; import java.lang.management.*; @@ -14,7 +14,7 @@ * Runnable thread to retrieve node thread's information and compiling it into * an array of NodeThreadInfo objects. */ -public class ThreadsDiagnostics implements Runnable, Diagnostics { +public class ThreadDiagnostics implements Runnable { private final String name; private final int monitorInterval; @@ -39,7 +39,7 @@ public class ThreadsDiagnostics implements Runnable, Diagnostics { * @param name Thread name * @param monitorInterval Sleep intervals to retrieve CPU usage */ - public ThreadsDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int monitorInterval) { + public ThreadDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int monitorInterval) { this.nodeStats = nodeStats; this.ticker = ticker; this.name = name; @@ -50,7 +50,7 @@ public ThreadsDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int m * @param nodeStats Used to retrieve data points * @param ticker Used to queue timed jobs */ - public ThreadsDiagnostics(NodeStats nodeStats, Ticker ticker) { + public ThreadDiagnostics(NodeStats nodeStats, Ticker ticker) { this(nodeStats, ticker, MONITOR_THREAD_NAME, MONITOR_INTERVAL); } diff --git a/src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java similarity index 100% rename from src/freenet/node/diagnostics/diagnostics/threads/NodeThreadInfo.java rename to src/freenet/node/diagnostics/threads/NodeThreadInfo.java From 784bdc25cbfa476e2d579e4a2eb946b55a1f5584 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:08:00 +0100 Subject: [PATCH 12/54] Create NodeDiagnostics and ThreadDiagnostics interfaces and default implementations --- .../clients/http/DiagnosticToadlet.java | 11 +++++----- src/freenet/node/Node.java | 12 +++++----- .../diagnostics/DefaultNodeDiagnostics.java | 22 +++++++------------ .../node/diagnostics/NodeDiagnostics.java | 8 ++++--- .../node/diagnostics/ThreadDiagnostics.java | 8 +++++-- .../threads/DefaultThreadDiagnostics.java | 12 +++++----- .../diagnostics/threads/NodeThreadInfo.java | 2 +- 7 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index af9467e0b4b..867604cf65f 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -20,7 +20,6 @@ import freenet.io.xfer.BlockReceiver; import freenet.io.xfer.BlockTransmitter; import freenet.l10n.BaseL10n; -import freenet.node.diagnostics.NodeDiagnostics; import freenet.node.Node; import freenet.node.NodeClientCore; import freenet.node.NodeStarter; @@ -30,8 +29,8 @@ import freenet.node.PeerNodeStatus; import freenet.node.RequestTracker; import freenet.node.Version; -import freenet.node.diagnostics.diagnostics.*; -import freenet.node.diagnostics.diagnostics.threads.*; +import freenet.node.diagnostics.*; +import freenet.node.diagnostics.threads.*; import freenet.node.stats.DataStoreInstanceType; import freenet.node.stats.DataStoreStats; import freenet.node.stats.StatsNotAvailableException; @@ -437,11 +436,11 @@ private StringBuilder threadsStats() { ) ); - ThreadsDiagnostics threadsDiagnostics = node + ThreadDiagnostics threadDiagnostics = node .getNodeDiagnostics() - .getThreadsDiagnostics(); + .getThreadDiagnostics(); - List threads = threadsDiagnostics.getThreads(); + List threads = threadDiagnostics.getThreads(); threads.sort( (o1, o2) -> Double.compare(o2.getCpuTime(), o1.getCpuTime()) ); diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index feb94fc30b9..04dbc1514f5 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -82,7 +82,7 @@ import freenet.keys.SSKVerifyException; import freenet.l10n.BaseL10n; import freenet.l10n.NodeL10n; -import freenet.node.diagnostics.NodeDiagnostics; +import freenet.node.diagnostics.DefaultNodeDiagnostics; import freenet.node.DarknetPeerNode.FRIEND_TRUST; import freenet.node.DarknetPeerNode.FRIEND_VISIBILITY; import freenet.node.NodeDispatcher.NodeDispatcherCallback; @@ -725,7 +725,7 @@ public String[] getPossibleValues() { public final SecurityLevels securityLevels; /** Diagnostics */ - private final NodeDiagnostics nodeDiagnostics; + private final DefaultNodeDiagnostics defaultNodeDiagnostics; // Things that's needed to keep track of public final PluginManager pluginManager; @@ -2606,7 +2606,7 @@ public void realRun() { new BandwidthManager(this).start(); - nodeDiagnostics = new NodeDiagnostics(this.nodeStats, this.ticker); + defaultNodeDiagnostics = new DefaultNodeDiagnostics(this.nodeStats, this.ticker); } private void peersOffersFrefFilesConfiguration(SubConfig nodeConfig, int configOptionSortOrder) { @@ -3172,7 +3172,7 @@ public void start(boolean noSwaps) throws NodeInitException { // Process any data in the extra peer data directory peers.readExtraPeerData(); - nodeDiagnostics.start(); + defaultNodeDiagnostics.start(); Logger.normal(this, "Started node"); @@ -4901,7 +4901,7 @@ DatabaseKey getDatabaseKey() { return databaseKey; } - public NodeDiagnostics getNodeDiagnostics() { - return nodeDiagnostics; + public DefaultNodeDiagnostics getNodeDiagnostics() { + return defaultNodeDiagnostics; } } diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java index a16d69014f2..e1f09237f0b 100644 --- a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -20,33 +20,27 @@ * is available through the public methods. * Some data pointers are obtained from NodeStats object. */ -public class NodeDiagnostics { - private enum DIAGNOSTICS { - THREADS - }; - - private final HashMap diagnostics = new HashMap<>(); +public class DefaultNodeDiagnostics implements NodeDiagnostics { + private final DefaultThreadDiagnostics defaultThreadDiagnostics; /** * @param nodeStats Used to retrieve data points. * @param ticker Used to queue timed jobs. */ - public NodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { - this.diagnostics.put( - DIAGNOSTICS.THREADS, - new ThreadsDiagnostics(nodeStats, ticker) - ); + public DefaultNodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { + defaultThreadDiagnostics = new DefaultThreadDiagnostics(nodeStats, ticker); } public void start() throws NodeInitException { - diagnostics.forEach((type, diagnostic) -> diagnostic.start()); + defaultThreadDiagnostics.start(); } /** * * @return List of threads registered in NodeStats.getThreads() */ - public ThreadsDiagnostics getThreadsDiagnostics() { - return (ThreadsDiagnostics)diagnostics.get(DIAGNOSTICS.THREADS); + @Override + public ThreadDiagnostics getThreadDiagnostics() { + return defaultThreadDiagnostics; } } diff --git a/src/freenet/node/diagnostics/NodeDiagnostics.java b/src/freenet/node/diagnostics/NodeDiagnostics.java index e27121accd1..bba852959e8 100644 --- a/src/freenet/node/diagnostics/NodeDiagnostics.java +++ b/src/freenet/node/diagnostics/NodeDiagnostics.java @@ -3,6 +3,8 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics; -public interface Diagnostics { - void start(); -} +import freenet.node.diagnostics.threads.*; + +public interface NodeDiagnostics { + ThreadDiagnostics getThreadDiagnostics(); +} \ No newline at end of file diff --git a/src/freenet/node/diagnostics/ThreadDiagnostics.java b/src/freenet/node/diagnostics/ThreadDiagnostics.java index e27121accd1..875ac9076dd 100644 --- a/src/freenet/node/diagnostics/ThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/ThreadDiagnostics.java @@ -3,6 +3,10 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.diagnostics; -public interface Diagnostics { - void start(); +import freenet.node.diagnostics.threads.*; + +import java.util.*; + +public interface ThreadDiagnostics { + List getThreads(); } diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 9f1e8936174..1c22f397fa2 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -14,7 +14,7 @@ * Runnable thread to retrieve node thread's information and compiling it into * an array of NodeThreadInfo objects. */ -public class ThreadDiagnostics implements Runnable { +public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final String name; private final int monitorInterval; @@ -27,7 +27,7 @@ public class ThreadDiagnostics implements Runnable { /** Sleep interval to calculate % CPU used by each thread */ private static final int CPU_SLEEP_INTERVAL = 1000; - List nodeThreadInfos = Collections.synchronizedList(new ArrayList<>()); + List nodeThreadInfo = Collections.synchronizedList(new ArrayList<>()); OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); @@ -39,7 +39,7 @@ public class ThreadDiagnostics implements Runnable { * @param name Thread name * @param monitorInterval Sleep intervals to retrieve CPU usage */ - public ThreadDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int monitorInterval) { + public DefaultThreadDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int monitorInterval) { this.nodeStats = nodeStats; this.ticker = ticker; this.name = name; @@ -50,7 +50,7 @@ public ThreadDiagnostics(NodeStats nodeStats, Ticker ticker, String name, int mo * @param nodeStats Used to retrieve data points * @param ticker Used to queue timed jobs */ - public ThreadDiagnostics(NodeStats nodeStats, Ticker ticker) { + public DefaultThreadDiagnostics(NodeStats nodeStats, Ticker ticker) { this(nodeStats, ticker, MONITOR_THREAD_NAME, MONITOR_INTERVAL); } @@ -126,7 +126,7 @@ public void run() { threads.add(nodeThreadInfo); } synchronized (this) { - nodeThreadInfos = threads; + nodeThreadInfo = threads; } scheduleNext(); @@ -136,6 +136,6 @@ public void run() { * @return List of Node threads */ public synchronized List getThreads() { - return new ArrayList<>(nodeThreadInfos); + return new ArrayList<>(nodeThreadInfo); } } diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index f5666ce5a65..23092127d36 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -1,7 +1,7 @@ /* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ -package freenet.node.diagnostics.diagnostics.threads; +package freenet.node.diagnostics.threads; /** * Class to wrap node thread information. From 4c1beef4067ad1fc15cf74dcfebef6fb66bc3c46 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:09:49 +0100 Subject: [PATCH 13/54] Reduce visibility for fields in ThreadDiagnostics --- .../diagnostics/threads/DefaultThreadDiagnostics.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 1c22f397fa2..8a76fdd7cb8 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -27,11 +27,11 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { /** Sleep interval to calculate % CPU used by each thread */ private static final int CPU_SLEEP_INTERVAL = 1000; - List nodeThreadInfo = Collections.synchronizedList(new ArrayList<>()); + private List nodeThreadInfo = Collections.synchronizedList(new ArrayList<>()); - OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); - ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); + private final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + private final RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); /** * @param nodeStats Used to retrieve data points From 0f81f05b7a29d0c58e8d54c4dba7ed1cb697614a Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:16:12 +0100 Subject: [PATCH 14/54] Use atomicReference for nodeThreadInfo list --- .../threads/DefaultThreadDiagnostics.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 8a76fdd7cb8..26df631f974 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -9,6 +9,7 @@ import java.lang.management.*; import java.util.*; +import java.util.concurrent.atomic.*; /** * Runnable thread to retrieve node thread's information and compiling it into @@ -27,7 +28,7 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { /** Sleep interval to calculate % CPU used by each thread */ private static final int CPU_SLEEP_INTERVAL = 1000; - private List nodeThreadInfo = Collections.synchronizedList(new ArrayList<>()); + private final AtomicReference> nodeThreadInfo = new AtomicReference<>(new ArrayList<>()); private final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); @@ -125,9 +126,8 @@ public void run() { threads.add(nodeThreadInfo); } - synchronized (this) { - nodeThreadInfo = threads; - } + + nodeThreadInfo.set(threads); scheduleNext(); } @@ -135,7 +135,7 @@ public void run() { /** * @return List of Node threads */ - public synchronized List getThreads() { - return new ArrayList<>(nodeThreadInfo); + public List getThreads() { + return new ArrayList<>(nodeThreadInfo.get()); } } From f6aa0c79916ea0e5fef4cd6ce47bf3f45471fa52 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:36:50 +0100 Subject: [PATCH 15/54] Use thread interval to build data points --- .../threads/DefaultThreadDiagnostics.java | 72 +++++++++++-------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 26df631f974..84e43e71638 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -22,12 +22,10 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final NodeStats nodeStats; private final Ticker ticker; - /** Interval in ms the ThreadMonitor is launched */ - private static final int MONITOR_INTERVAL = 10000; + /** Sleep interval to calculate % CPU used by each thread */ + private static final int MONITOR_INTERVAL = 1000; private static final String MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; - /** Sleep interval to calculate % CPU used by each thread */ - private static final int CPU_SLEEP_INTERVAL = 1000; private final AtomicReference> nodeThreadInfo = new AtomicReference<>(new ArrayList<>()); private final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); @@ -73,24 +71,25 @@ private void scheduleNext() { scheduleNext(monitorInterval); } + private final Map threadCPU = new HashMap<>(); + private long initialUptime = -1; + @Override public void run() { - Map threadCPU = new HashMap<>(); - - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - threadCPU.put( - info.getThreadId(), - threadMxBean.getThreadCpuTime( - info.getThreadId() - ) - ); - } - - long initialUptime = runtimeMxBean.getUptime(); + if (initialUptime == -1) { + initialUptime = runtimeMxBean.getUptime(); + + for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { + threadCPU.put( + info.getThreadId(), + threadMxBean.getThreadCpuTime( + info.getThreadId() + ) + ); + } - try { - Thread.sleep(CPU_SLEEP_INTERVAL); - } catch (InterruptedException ignored) { + scheduleNext(); + return; } for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { @@ -105,31 +104,42 @@ public void run() { ); } + nodeThreadInfo.set(buildThreadList()); + + initialUptime = -1; + threadCPU.clear(); + + scheduleNext(); + } + + /** + * + * @return + */ + private List buildThreadList() { + List threads = new ArrayList<>(); long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000F; - List threads = new ArrayList<>(); - for (Thread t : nodeStats.getThreads()) { - if (t == null) { + for (Thread thread : nodeStats.getThreads()) { + if (thread == null) { continue; } - double cpuUsage = threadCPU.getOrDefault(t.getId(), 0L) / totalElapsedUptime; + double cpuUsage = threadCPU.getOrDefault(thread.getId(), 0L) / totalElapsedUptime; NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( - t.getId(), - t.getName(), - t.getPriority(), - t.getThreadGroup().getName(), - t.getState().toString(), + thread.getId(), + thread.getName(), + thread.getPriority(), + thread.getThreadGroup().getName(), + thread.getState().toString(), cpuUsage ); threads.add(nodeThreadInfo); } - nodeThreadInfo.set(threads); - - scheduleNext(); + return threads; } /** From e8c1a5af04bd12fc86abaa1ed8f248b046b30240 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:47:53 +0100 Subject: [PATCH 16/54] Fix calculation CPU time percentage --- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 84e43e71638..537deda5b01 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -119,14 +119,14 @@ public void run() { private List buildThreadList() { List threads = new ArrayList<>(); long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; - double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000F; + double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000d; for (Thread thread : nodeStats.getThreads()) { if (thread == null) { continue; } - double cpuUsage = threadCPU.getOrDefault(thread.getId(), 0L) / totalElapsedUptime; + double cpuUsage = (threadCPU.getOrDefault(thread.getId(), 0L) / totalElapsedUptime) * 100; NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( thread.getId(), thread.getName(), From 9aec574b195a62f2213efc32b0496e81bfad6414 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 01:58:05 +0100 Subject: [PATCH 17/54] Use NodeDiagnostics type interface rather than default implementation --- src/freenet/node/Node.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 04dbc1514f5..0e3b4e248fb 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -38,6 +38,7 @@ import java.util.Set; import freenet.config.*; +import freenet.node.diagnostics.*; import freenet.node.useralerts.*; import org.tanukisoftware.wrapper.WrapperManager; @@ -82,7 +83,6 @@ import freenet.keys.SSKVerifyException; import freenet.l10n.BaseL10n; import freenet.l10n.NodeL10n; -import freenet.node.diagnostics.DefaultNodeDiagnostics; import freenet.node.DarknetPeerNode.FRIEND_TRUST; import freenet.node.DarknetPeerNode.FRIEND_VISIBILITY; import freenet.node.NodeDispatcher.NodeDispatcherCallback; @@ -725,7 +725,7 @@ public String[] getPossibleValues() { public final SecurityLevels securityLevels; /** Diagnostics */ - private final DefaultNodeDiagnostics defaultNodeDiagnostics; + private final NodeDiagnostics nodeDiagnostics; // Things that's needed to keep track of public final PluginManager pluginManager; @@ -2606,7 +2606,7 @@ public void realRun() { new BandwidthManager(this).start(); - defaultNodeDiagnostics = new DefaultNodeDiagnostics(this.nodeStats, this.ticker); + nodeDiagnostics = new DefaultNodeDiagnostics(this.nodeStats, this.ticker); } private void peersOffersFrefFilesConfiguration(SubConfig nodeConfig, int configOptionSortOrder) { @@ -3172,7 +3172,7 @@ public void start(boolean noSwaps) throws NodeInitException { // Process any data in the extra peer data directory peers.readExtraPeerData(); - defaultNodeDiagnostics.start(); + ((DefaultNodeDiagnostics)nodeDiagnostics).start(); Logger.normal(this, "Started node"); @@ -4901,7 +4901,7 @@ DatabaseKey getDatabaseKey() { return databaseKey; } - public DefaultNodeDiagnostics getNodeDiagnostics() { - return defaultNodeDiagnostics; + public NodeDiagnostics getNodeDiagnostics() { + return nodeDiagnostics; } } From b72db1b73d4891badc31f549ad7c8d555430e644 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 16:24:10 +0100 Subject: [PATCH 18/54] Use DefaultNodeDiagnostics implementation --- src/freenet/node/Node.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 0e3b4e248fb..9f7167f7843 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -725,7 +725,7 @@ public String[] getPossibleValues() { public final SecurityLevels securityLevels; /** Diagnostics */ - private final NodeDiagnostics nodeDiagnostics; + private final DefaultNodeDiagnostics nodeDiagnostics; // Things that's needed to keep track of public final PluginManager pluginManager; @@ -3172,7 +3172,7 @@ public void start(boolean noSwaps) throws NodeInitException { // Process any data in the extra peer data directory peers.readExtraPeerData(); - ((DefaultNodeDiagnostics)nodeDiagnostics).start(); + nodeDiagnostics.start(); Logger.normal(this, "Started node"); From 99835a920c11e765e18ac9cf6466a11f6d16a80d Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 16:25:01 +0100 Subject: [PATCH 19/54] Remove unnecessary finals in contructor --- src/freenet/node/diagnostics/DefaultNodeDiagnostics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java index e1f09237f0b..e92c9c42fd4 100644 --- a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -27,7 +27,7 @@ public class DefaultNodeDiagnostics implements NodeDiagnostics { * @param nodeStats Used to retrieve data points. * @param ticker Used to queue timed jobs. */ - public DefaultNodeDiagnostics(final NodeStats nodeStats, final Ticker ticker) { + public DefaultNodeDiagnostics(NodeStats nodeStats, Ticker ticker) { defaultThreadDiagnostics = new DefaultThreadDiagnostics(nodeStats, ticker); } From 6282ffdbe2ca12f59eb868296199ec5a7a266888 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 16:27:45 +0100 Subject: [PATCH 20/54] Remove unnecessary throw exception --- src/freenet/node/diagnostics/DefaultNodeDiagnostics.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java index e92c9c42fd4..09dca9961ac 100644 --- a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -6,9 +6,6 @@ import freenet.node.diagnostics.threads.*; import freenet.support.Ticker; import freenet.node.NodeStats; -import freenet.node.NodeInitException; - -import java.util.HashMap; /** * @author desyncr @@ -31,7 +28,7 @@ public DefaultNodeDiagnostics(NodeStats nodeStats, Ticker ticker) { defaultThreadDiagnostics = new DefaultThreadDiagnostics(nodeStats, ticker); } - public void start() throws NodeInitException { + public void start() { defaultThreadDiagnostics.start(); } From 8946cc00f182950fc0c6afcc732225e559a1ae5f Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 16:28:25 +0100 Subject: [PATCH 21/54] Remove unnecessary copy --- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 537deda5b01..78e9ac09bf0 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -146,6 +146,6 @@ private List buildThreadList() { * @return List of Node threads */ public List getThreads() { - return new ArrayList<>(nodeThreadInfo.get()); + return nodeThreadInfo.get(); } } From 730f4d7614197b104bf82b45532f0938a200ce74 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 17 Apr 2021 16:56:40 +0100 Subject: [PATCH 22/54] Fix CPU time percentaje calculation --- .../diagnostics/threads/DefaultThreadDiagnostics.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 78e9ac09bf0..80238dca335 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -71,6 +71,7 @@ private void scheduleNext() { scheduleNext(monitorInterval); } + /** Map */ private final Map threadCPU = new HashMap<>(); private long initialUptime = -1; @@ -113,20 +114,19 @@ public void run() { } /** - * - * @return + * @return List of NodeThreadInfo */ private List buildThreadList() { List threads = new ArrayList<>(); - long elapsedUptime = runtimeMxBean.getUptime() - initialUptime; - double totalElapsedUptime = (elapsedUptime * operatingSystemMXBean.getAvailableProcessors()) * 10000d; + double elapsedUptime = runtimeMxBean.getUptime() - initialUptime; + double totalElapsedUptime = elapsedUptime * operatingSystemMXBean.getAvailableProcessors(); for (Thread thread : nodeStats.getThreads()) { if (thread == null) { continue; } - double cpuUsage = (threadCPU.getOrDefault(thread.getId(), 0L) / totalElapsedUptime) * 100; + double cpuUsage = (threadCPU.getOrDefault(thread.getId(), 0L) / 1000000d) / totalElapsedUptime * 100; NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( thread.getId(), thread.getName(), From 05914a35a133be4447414b87e9fa34209b11fbc4 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 24 Apr 2021 02:28:32 +0100 Subject: [PATCH 23/54] Use Comparator class to simplify threads sorting --- src/freenet/clients/http/DiagnosticToadlet.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 867604cf65f..1f10cb7b38f 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -441,9 +441,7 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); List threads = threadDiagnostics.getThreads(); - threads.sort( - (o1, o2) -> Double.compare(o2.getCpuTime(), o1.getCpuTime()) - ); + threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); for (NodeThreadInfo thread : threads) { String line = String.format( From 512dcd350870b9b5f8398605e7825e001511d710 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 24 Apr 2021 03:06:26 +0100 Subject: [PATCH 24/54] Use single loop to calculate delta and display --- .../threads/DefaultThreadDiagnostics.java | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 80238dca335..728d81c1d8d 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -71,44 +71,24 @@ private void scheduleNext() { scheduleNext(monitorInterval); } - /** Map */ - private final Map threadCPU = new HashMap<>(); - private long initialUptime = -1; + private final AtomicReference> threadCPU = new AtomicReference<>(new HashMap<>()); + private long lastCycle = -1; @Override public void run() { - if (initialUptime == -1) { - initialUptime = runtimeMxBean.getUptime(); - - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - threadCPU.put( - info.getThreadId(), - threadMxBean.getThreadCpuTime( - info.getThreadId() - ) - ); - } - - scheduleNext(); - return; - } - + Map delta = new HashMap<>(threadCPU.get()); + Map threads = new HashMap<>(); for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - Long prev = threadCPU.get(info.getThreadId()); - if (prev == null) { - continue; - } + long threadId = info.getThreadId(); + long cpuTime = threadMxBean.getThreadCpuTime(threadId); - threadCPU.put( - info.getThreadId(), - threadMxBean.getThreadCpuTime(info.getThreadId()) - prev - ); + delta.put(threadId, cpuTime - delta.getOrDefault(threadId, 0L)); + threads.put(threadId, cpuTime); } - nodeThreadInfo.set(buildThreadList()); - - initialUptime = -1; - threadCPU.clear(); + threadCPU.set(threads); + nodeThreadInfo.set(buildThreadList(delta)); + lastCycle = runtimeMxBean.getUptime(); scheduleNext(); } @@ -116,17 +96,16 @@ public void run() { /** * @return List of NodeThreadInfo */ - private List buildThreadList() { + private List buildThreadList(Map delta) { List threads = new ArrayList<>(); - double elapsedUptime = runtimeMxBean.getUptime() - initialUptime; + double elapsedUptime = runtimeMxBean.getUptime() - lastCycle; double totalElapsedUptime = elapsedUptime * operatingSystemMXBean.getAvailableProcessors(); - for (Thread thread : nodeStats.getThreads()) { if (thread == null) { continue; } - double cpuUsage = (threadCPU.getOrDefault(thread.getId(), 0L) / 1000000d) / totalElapsedUptime * 100; + double cpuUsage = (delta.getOrDefault(thread.getId(), 0L) / 1000000d) / totalElapsedUptime * 100; NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( thread.getId(), thread.getName(), From 19962234e2eefc986453eb3e1e7e2234708ff810 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 24 Apr 2021 03:34:50 +0100 Subject: [PATCH 25/54] Rename private field to follow convention --- .../node/diagnostics/threads/NodeThreadInfo.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index 23092127d36..87f22920919 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -12,7 +12,7 @@ public class NodeThreadInfo { private final int prio; private final String groupName; private final String state; - private final double cpu_time; + private final double cpuTime; /** * @param id Thread ID @@ -20,15 +20,15 @@ public class NodeThreadInfo { * @param prio Thread priority * @param groupName Thread's group name * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) - * @param cpu_time Thread's % of CPU time used + * @param cpuTime Thread's % of CPU time used */ - NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpu_time) { + NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpuTime) { this.id = id; this.name = name; this.prio = prio; this.groupName = groupName; this.state = state; - this.cpu_time = cpu_time; + this.cpuTime = cpuTime; } public long getId() { @@ -52,6 +52,6 @@ public String getState() { } public double getCpuTime() { - return cpu_time; + return cpuTime; } } \ No newline at end of file From e3c24ec9827bcce30c58af59e06a24a679b40803 Mon Sep 17 00:00:00 2001 From: DC* Date: Tue, 27 Apr 2021 23:52:21 +0100 Subject: [PATCH 26/54] Show percentage cpu time between process threads --- .../threads/DefaultThreadDiagnostics.java | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 728d81c1d8d..b74c85de1d0 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -28,9 +28,7 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final AtomicReference> nodeThreadInfo = new AtomicReference<>(new ArrayList<>()); - private final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - private final RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); /** * @param nodeStats Used to retrieve data points @@ -71,24 +69,19 @@ private void scheduleNext() { scheduleNext(monitorInterval); } - private final AtomicReference> threadCPU = new AtomicReference<>(new HashMap<>()); - private long lastCycle = -1; - @Override public void run() { - Map delta = new HashMap<>(threadCPU.get()); Map threads = new HashMap<>(); + double totalCpuTime = 0d; for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { long threadId = info.getThreadId(); long cpuTime = threadMxBean.getThreadCpuTime(threadId); - delta.put(threadId, cpuTime - delta.getOrDefault(threadId, 0L)); + totalCpuTime += cpuTime; threads.put(threadId, cpuTime); } - threadCPU.set(threads); - nodeThreadInfo.set(buildThreadList(delta)); - lastCycle = runtimeMxBean.getUptime(); + nodeThreadInfo.set(buildThreadList(threads, totalCpuTime)); scheduleNext(); } @@ -96,16 +89,14 @@ public void run() { /** * @return List of NodeThreadInfo */ - private List buildThreadList(Map delta) { - List threads = new ArrayList<>(); - double elapsedUptime = runtimeMxBean.getUptime() - lastCycle; - double totalElapsedUptime = elapsedUptime * operatingSystemMXBean.getAvailableProcessors(); + private List buildThreadList(Map threads, double totalCpuTime) { + List threadInfo = new ArrayList<>(); for (Thread thread : nodeStats.getThreads()) { if (thread == null) { continue; } - double cpuUsage = (delta.getOrDefault(thread.getId(), 0L) / 1000000d) / totalElapsedUptime * 100; + double cpuUsage = ((threads.getOrDefault(thread.getId(), 0L) / totalCpuTime) * 100); NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( thread.getId(), thread.getName(), @@ -115,10 +106,10 @@ private List buildThreadList(Map delta) { cpuUsage ); - threads.add(nodeThreadInfo); + threadInfo.add(nodeThreadInfo); } - return threads; + return threadInfo; } /** From 170961dc4447884b1e65c1a691be6268e137cbd3 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 29 Apr 2021 02:12:17 +0100 Subject: [PATCH 27/54] Separate presentation from actual data for NodeThreadInfo --- .../clients/http/DiagnosticToadlet.java | 5 +- .../threads/DefaultThreadDiagnostics.java | 53 ++++++------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 1f10cb7b38f..d7c0259de06 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -441,6 +441,7 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); List threads = threadDiagnostics.getThreads(); + double totalCpuTime = threads.stream().mapToDouble(NodeThreadInfo::getCpuTime).sum(); threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); for (NodeThreadInfo thread : threads) { @@ -449,9 +450,9 @@ private StringBuilder threadsStats() { thread.getId(), thread.getName().substring(0, Math.min(60, thread.getName().length())), thread.getPrio(), - thread.getGroupName(), + thread.getGroupName().substring(0, Math.min(10, thread.getGroupName().length())), thread.getState(), - thread.getCpuTime() + thread.getCpuTime() / totalCpuTime * 100 ); sb.append(line); } diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index b74c85de1d0..36fca1b25cf 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -10,6 +10,7 @@ import java.lang.management.*; import java.util.*; import java.util.concurrent.atomic.*; +import java.util.stream.*; /** * Runnable thread to retrieve node thread's information and compiling it into @@ -71,47 +72,25 @@ private void scheduleNext() { @Override public void run() { - Map threads = new HashMap<>(); - double totalCpuTime = 0d; - for (ThreadInfo info : threadMxBean.dumpAllThreads(false, false)) { - long threadId = info.getThreadId(); - long cpuTime = threadMxBean.getThreadCpuTime(threadId); - - totalCpuTime += cpuTime; - threads.put(threadId, cpuTime); - } - - nodeThreadInfo.set(buildThreadList(threads, totalCpuTime)); + nodeThreadInfo.set( + Arrays.stream(nodeStats.getThreads()) + .filter(Objects::nonNull) + .map(thread -> + new NodeThreadInfo( + thread.getId(), + thread.getName(), + thread.getPriority(), + thread.getThreadGroup().getName(), + thread.getState().toString(), + threadMxBean.getThreadCpuTime(thread.getId()) + ) + ) + .collect(Collectors.toList()) + ); scheduleNext(); } - /** - * @return List of NodeThreadInfo - */ - private List buildThreadList(Map threads, double totalCpuTime) { - List threadInfo = new ArrayList<>(); - for (Thread thread : nodeStats.getThreads()) { - if (thread == null) { - continue; - } - - double cpuUsage = ((threads.getOrDefault(thread.getId(), 0L) / totalCpuTime) * 100); - NodeThreadInfo nodeThreadInfo = new NodeThreadInfo( - thread.getId(), - thread.getName(), - thread.getPriority(), - thread.getThreadGroup().getName(), - thread.getState().toString(), - cpuUsage - ); - - threadInfo.add(nodeThreadInfo); - } - - return threadInfo; - } - /** * @return List of Node threads */ From ca9e9f33be1b6a2e8485dcc40ea05e2dd69b0949 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 29 Apr 2021 02:35:41 +0100 Subject: [PATCH 28/54] Compute % CPU by calculate the total CPU time from all threads, not only nodestat's --- src/freenet/clients/http/DiagnosticToadlet.java | 16 +++++++++++----- .../threads/DefaultThreadDiagnostics.java | 8 ++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index d7c0259de06..74349605692 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -2,13 +2,13 @@ import java.io.File; import java.io.IOException; +import java.lang.management.*; import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Map; -import java.util.List; +import java.util.*; +import java.util.stream.*; + import freenet.client.HighLevelSimpleClient; import freenet.client.async.PersistenceDisabledException; import freenet.clients.fcp.DownloadRequestStatus; @@ -441,7 +441,13 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); List threads = threadDiagnostics.getThreads(); - double totalCpuTime = threads.stream().mapToDouble(NodeThreadInfo::getCpuTime).sum(); + //double totalCpuTime = threads.stream().mapToDouble(NodeThreadInfo::getCpuTime).sum(); + ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + double totalCpuTime = Arrays.stream(threadMxBean.dumpAllThreads(false, false)) + .mapToDouble( + (t) -> threadMxBean.getThreadCpuTime(t.getThreadId()) + ).sum(); + threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); for (NodeThreadInfo thread : threads) { diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 36fca1b25cf..798544c246c 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -72,8 +72,7 @@ private void scheduleNext() { @Override public void run() { - nodeThreadInfo.set( - Arrays.stream(nodeStats.getThreads()) + List threads = Arrays.stream(nodeStats.getThreads()) .filter(Objects::nonNull) .map(thread -> new NodeThreadInfo( @@ -85,8 +84,9 @@ public void run() { threadMxBean.getThreadCpuTime(thread.getId()) ) ) - .collect(Collectors.toList()) - ); + .collect(Collectors.toList()); + + nodeThreadInfo.set(threads); scheduleNext(); } From d8241d32e4e7d41b2be436510f43c2a6f4ec3a22 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 29 Apr 2021 02:36:38 +0100 Subject: [PATCH 29/54] Renaming internal variables --- src/freenet/clients/http/DiagnosticToadlet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 74349605692..cb8727c37b3 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -441,11 +441,11 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); List threads = threadDiagnostics.getThreads(); - //double totalCpuTime = threads.stream().mapToDouble(NodeThreadInfo::getCpuTime).sum(); + ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); double totalCpuTime = Arrays.stream(threadMxBean.dumpAllThreads(false, false)) .mapToDouble( - (t) -> threadMxBean.getThreadCpuTime(t.getThreadId()) + (thread) -> threadMxBean.getThreadCpuTime(thread.getThreadId()) ).sum(); threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); From 1c5c52912bc19a30273661b16e01a24173b9b24b Mon Sep 17 00:00:00 2001 From: DC* Date: Fri, 30 Apr 2021 00:32:18 +0100 Subject: [PATCH 30/54] Re-introduce delta CPU Time and simplify code structure --- .../clients/http/DiagnosticToadlet.java | 12 +++--- .../threads/DefaultThreadDiagnostics.java | 43 ++++++++++++++----- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index cb8727c37b3..35af8bc54ee 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -441,15 +441,13 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); List threads = threadDiagnostics.getThreads(); - - ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); - double totalCpuTime = Arrays.stream(threadMxBean.dumpAllThreads(false, false)) - .mapToDouble( - (thread) -> threadMxBean.getThreadCpuTime(thread.getThreadId()) - ).sum(); - threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); + double totalCpuTime = threads + .stream() + .mapToDouble(NodeThreadInfo::getCpuTime) + .sum(); + for (NodeThreadInfo thread : threads) { String line = String.format( "%5s %-60s %5s %10s %-20s %.2f%n", diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 798544c246c..a262cb7dcb5 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -29,7 +29,7 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final AtomicReference> nodeThreadInfo = new AtomicReference<>(new ArrayList<>()); - private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); /** * @param nodeStats Used to retrieve data points @@ -70,18 +70,46 @@ private void scheduleNext() { scheduleNext(monitorInterval); } + private final HashMap threadCpu = new HashMap<>(); + + /** + * Calculate the "delta" CPU time for a given thread. This method keeps + * track of the previous CPU Time and calculates the difference between that + * snapshot and the current CPU Time. + * + * If there's no previous snapshot of CPU Time for the thread this method + * will return 0. + * + * @param threadId Thread ID to get the CPU usage + * @return Delta CPU time. + */ + private double getCpuTimeDelta(long threadId) { + double current = threadMxBean.getThreadCpuTime(threadId); + + double cpuUsage = current - threadCpu.getOrDefault(threadId, current); + threadCpu.put(threadId, current); + + return cpuUsage; + } + + /** + * @return List of Node threads + */ + public List getThreads() { + return nodeThreadInfo.get(); + } + @Override public void run() { List threads = Arrays.stream(nodeStats.getThreads()) .filter(Objects::nonNull) - .map(thread -> - new NodeThreadInfo( + .map(thread -> new NodeThreadInfo( thread.getId(), thread.getName(), thread.getPriority(), thread.getThreadGroup().getName(), thread.getState().toString(), - threadMxBean.getThreadCpuTime(thread.getId()) + getCpuTimeDelta(thread.getId()) ) ) .collect(Collectors.toList()); @@ -90,11 +118,4 @@ public void run() { scheduleNext(); } - - /** - * @return List of Node threads - */ - public List getThreads() { - return nodeThreadInfo.get(); - } } From 2d36d9e9a8eb0d655aca3bc1720baedbb8d77a23 Mon Sep 17 00:00:00 2001 From: DC* Date: Fri, 30 Apr 2021 01:09:42 +0100 Subject: [PATCH 31/54] Output formatting in DiagnosticToadlet --- .../clients/http/DiagnosticToadlet.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 35af8bc54ee..45216ef54cd 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -412,7 +412,6 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { textBuilder.append("\n"); // drawThreadPriorityStatsBox - textBuilder.append("Threads:\n"); textBuilder.append(threadsStats()); textBuilder.append("\n"); @@ -423,6 +422,21 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { private StringBuilder threadsStats() { StringBuilder sb = new StringBuilder(); + + ThreadDiagnostics threadDiagnostics = node + .getNodeDiagnostics() + .getThreadDiagnostics(); + + List threads = threadDiagnostics.getThreads(); + threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); + + double totalCpuTime = Math.max(1, threads + .stream() + .mapToDouble(NodeThreadInfo::getCpuTime) + .sum()); + + sb.append(String.format("Threads (%d):%n", threads.size())); + // ID, Name, Priority, Group (system, main), Status, % CPU sb.append( String.format( @@ -436,18 +450,6 @@ private StringBuilder threadsStats() { ) ); - ThreadDiagnostics threadDiagnostics = node - .getNodeDiagnostics() - .getThreadDiagnostics(); - - List threads = threadDiagnostics.getThreads(); - threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); - - double totalCpuTime = threads - .stream() - .mapToDouble(NodeThreadInfo::getCpuTime) - .sum(); - for (NodeThreadInfo thread : threads) { String line = String.format( "%5s %-60s %5s %10s %-20s %.2f%n", From 56c9e0d4cafcb8b15cfc44506ac15ad4146ca452 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 2 May 2021 02:40:31 +0100 Subject: [PATCH 32/54] Handle case when thread.getThreadGroup returns null --- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index a262cb7dcb5..e86f50fddd1 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -103,6 +103,7 @@ public List getThreads() { public void run() { List threads = Arrays.stream(nodeStats.getThreads()) .filter(Objects::nonNull) + .filter(thread -> thread.getThreadGroup() != null) .map(thread -> new NodeThreadInfo( thread.getId(), thread.getName(), From dd2ccffdd63c2f02994568960e18a1c3809e5fd0 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 9 May 2021 20:04:39 +0100 Subject: [PATCH 33/54] Remove long -> double coercion Purge inactive threads from threadsCpu list --- .../threads/DefaultThreadDiagnostics.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index e86f50fddd1..db0ea21ea5b 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -70,7 +70,7 @@ private void scheduleNext() { scheduleNext(monitorInterval); } - private final HashMap threadCpu = new HashMap<>(); + private final HashMap threadCpu = new HashMap<>(); /** * Calculate the "delta" CPU time for a given thread. This method keeps @@ -84,14 +84,28 @@ private void scheduleNext() { * @return Delta CPU time. */ private double getCpuTimeDelta(long threadId) { - double current = threadMxBean.getThreadCpuTime(threadId); + long current = threadMxBean.getThreadCpuTime(threadId); - double cpuUsage = current - threadCpu.getOrDefault(threadId, current); + long cpuUsage = current - threadCpu.getOrDefault(threadId, current); threadCpu.put(threadId, current); return cpuUsage; } + /** + * Remove threads that aren't present in the last snapshot. + * + * @param threads List of active threads. + */ + private void purgeInactiveThreads(List threads) { + List activeThreads = threads.stream() + .map(NodeThreadInfo::getId) + .collect(Collectors.toList()); + + threadCpu.keySet() + .removeIf(key -> !activeThreads.contains(key)); + } + /** * @return List of Node threads */ @@ -116,6 +130,7 @@ public void run() { .collect(Collectors.toList()); nodeThreadInfo.set(threads); + purgeInactiveThreads(threads); scheduleNext(); } From 9d93258e223ae5d6a0dfd73c11bf3955fbfc7577 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 9 May 2021 20:35:24 +0100 Subject: [PATCH 34/54] Use NodeThreadSnapshot to hold thread list, total CPU and interval --- .../clients/http/DiagnosticToadlet.java | 10 ++-- .../node/diagnostics/ThreadDiagnostics.java | 4 +- .../threads/DefaultThreadDiagnostics.java | 44 ++++++++++------- .../threads/NodeThreadSnapshot.java | 48 +++++++++++++++++++ 4 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 45216ef54cd..10ecb2afbf6 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -427,13 +427,11 @@ private StringBuilder threadsStats() { .getNodeDiagnostics() .getThreadDiagnostics(); - List threads = threadDiagnostics.getThreads(); - threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); + NodeThreadSnapshot threadSnapshot = threadDiagnostics.getThreadSnapshot(); + double totalCpuTime = threadSnapshot.getTotalCpuTime(); - double totalCpuTime = Math.max(1, threads - .stream() - .mapToDouble(NodeThreadInfo::getCpuTime) - .sum()); + List threads = threadSnapshot.getThreads(); + threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); sb.append(String.format("Threads (%d):%n", threads.size())); diff --git a/src/freenet/node/diagnostics/ThreadDiagnostics.java b/src/freenet/node/diagnostics/ThreadDiagnostics.java index 875ac9076dd..944d79ff92b 100644 --- a/src/freenet/node/diagnostics/ThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/ThreadDiagnostics.java @@ -5,8 +5,6 @@ import freenet.node.diagnostics.threads.*; -import java.util.*; - public interface ThreadDiagnostics { - List getThreads(); + NodeThreadSnapshot getThreadSnapshot(); } diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index db0ea21ea5b..1a8a3019c13 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -24,13 +24,15 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final Ticker ticker; /** Sleep interval to calculate % CPU used by each thread */ - private static final int MONITOR_INTERVAL = 1000; - private static final String MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; - - private final AtomicReference> nodeThreadInfo = new AtomicReference<>(new ArrayList<>()); + private static final int DEFAULT_MONITOR_INTERVAL = 1000; + private static final String DEFAULT_MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; + private final AtomicReference nodeThreadSnapshot = new AtomicReference<>(); private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); + /** Map to track thread's CPU differences between intervals of time */ + private final HashMap threadCpu = new HashMap<>(); + /** * @param nodeStats Used to retrieve data points * @param ticker Used to queue timed jobs @@ -44,14 +46,26 @@ public DefaultThreadDiagnostics(NodeStats nodeStats, Ticker ticker, String name, this.monitorInterval = monitorInterval; } - /* + /** * @param nodeStats Used to retrieve data points * @param ticker Used to queue timed jobs */ public DefaultThreadDiagnostics(NodeStats nodeStats, Ticker ticker) { - this(nodeStats, ticker, MONITOR_THREAD_NAME, MONITOR_INTERVAL); + this(nodeStats, ticker, DEFAULT_MONITOR_THREAD_NAME, DEFAULT_MONITOR_INTERVAL); } + /** + * @return Current snapshot. + */ + public NodeThreadSnapshot getThreadSnapshot() { + return nodeThreadSnapshot.get(); + } + + /** + * Schedule this class execution in seconds. + * + * @param interval Time internal in seconds. + */ private void scheduleNext(int interval) { ticker.queueTimedJob( this, @@ -62,6 +76,9 @@ private void scheduleNext(int interval) { ); } + /** + * Start the execution. + */ public void start() { scheduleNext(0); } @@ -70,8 +87,6 @@ private void scheduleNext() { scheduleNext(monitorInterval); } - private final HashMap threadCpu = new HashMap<>(); - /** * Calculate the "delta" CPU time for a given thread. This method keeps * track of the previous CPU Time and calculates the difference between that @@ -106,13 +121,6 @@ private void purgeInactiveThreads(List threads) { .removeIf(key -> !activeThreads.contains(key)); } - /** - * @return List of Node threads - */ - public List getThreads() { - return nodeThreadInfo.get(); - } - @Override public void run() { List threads = Arrays.stream(nodeStats.getThreads()) @@ -129,9 +137,11 @@ public void run() { ) .collect(Collectors.toList()); - nodeThreadInfo.set(threads); - purgeInactiveThreads(threads); + nodeThreadSnapshot.set( + new NodeThreadSnapshot(threads, monitorInterval) + ); + purgeInactiveThreads(threads); scheduleNext(); } } diff --git a/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java b/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java new file mode 100644 index 00000000000..22c5591d7a6 --- /dev/null +++ b/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java @@ -0,0 +1,48 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.node.diagnostics.threads; + +import java.util.ArrayList; +import java.util.List; + +public class NodeThreadSnapshot { + private final List threads; + private final double totalCpuTime; + private final int interval; + + /** + * @param threads List of threads for this snapshot. + */ + public NodeThreadSnapshot(List threads, int interval) { + this.threads = new ArrayList<>(threads); + + totalCpuTime = Math.max(1, threads + .stream() + .mapToDouble(NodeThreadInfo::getCpuTime) + .sum()); + + this.interval = interval; + } + + /** + * @return The snapshot's thread list. + */ + public List getThreads() { + return new ArrayList<>(threads); + } + + /** + * @return The calculated total CPU time from the snapshot's thread list. + */ + public double getTotalCpuTime() { + return totalCpuTime; + } + + /** + * @return Snapshot interval. + */ + public int getInterval() { + return interval; + } +} From e52fc35c6b863a3dd48cc6e14dcb192ce49611aa Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 9 May 2021 21:06:19 +0100 Subject: [PATCH 35/54] Update ConfigToadlet to support enabling/disabling node diagnostics module --- .../clients/http/DiagnosticToadlet.java | 17 ++++----- src/freenet/l10n/freenet.l10n.en.properties | 2 ++ src/freenet/node/Node.java | 35 +++++++++++++++++-- .../diagnostics/DefaultNodeDiagnostics.java | 4 +++ .../threads/DefaultThreadDiagnostics.java | 4 +++ 5 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 10ecb2afbf6..d5e02ff80dc 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -176,7 +176,7 @@ public void handleMethodGET(URI uri, HTTPRequest request, ToadletContext ctx) th if ((numTransferringRequests == 0) && (numCHKRequests == 0) && (numSSKRequests == 0) && (numCHKInserts == 0) && (numSSKInserts == 0) && - (numTransferringRequestHandlers == 0) && + (numTransferringRequestHandlers == 0) && (numCHKOfferReplys == 0) && (numSSKOfferReplys == 0)) { textBuilder.append(l10n("noRequests")).append("\n"); } else { @@ -339,14 +339,14 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { long totalBytesSentNodeToNode = node.nodeStats.getNodeToNodeBytesSent(); long totalBytesSentAllocationNotices = node.nodeStats.getAllocationNoticesBytesSent(); long totalBytesSentFOAF = node.nodeStats.getFOAFBytesSent(); - long totalBytesSentRemaining = total[0] - + long totalBytesSentRemaining = total[0] - (totalPayload + totalBytesSentCHKRequests + totalBytesSentSSKRequests + totalBytesSentCHKInserts + totalBytesSentSSKInserts + - totalBytesSentOfferedKeys + totalBytesSendOffers + totalBytesSentSwapOutput + + totalBytesSentOfferedKeys + totalBytesSendOffers + totalBytesSentSwapOutput + totalBytesSentAuth + totalBytesSentAckOnly + totalBytesSentResends + - totalBytesSentUOM + totalBytesSentAnnounce + + totalBytesSentUOM + totalBytesSentAnnounce + totalBytesSentRoutingStatus + totalBytesSentNetworkColoring + totalBytesSentPing + - totalBytesSentProbeRequest + totalBytesSentRouted + totalBytesSentDisconn + + totalBytesSentProbeRequest + totalBytesSentRouted + totalBytesSentDisconn + totalBytesSentInitial + totalBytesSentChangedIP + totalBytesSentNodeToNode + totalBytesSentAllocationNotices + totalBytesSentFOAF); textBuilder.append(l10n("requestOutput", new String[] { "chk", "ssk" }, new String[] { SizeUtil.formatSize(totalBytesSentCHKRequests, true), SizeUtil.formatSize(totalBytesSentSSKRequests, true) })).append("\n"); textBuilder.append(l10n("insertOutput", new String[] { "chk", "ssk" }, new String[] { SizeUtil.formatSize(totalBytesSentCHKInserts, true), SizeUtil.formatSize(totalBytesSentSSKInserts, true) })).append("\n"); @@ -412,9 +412,10 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { textBuilder.append("\n"); // drawThreadPriorityStatsBox - textBuilder.append(threadsStats()); - - textBuilder.append("\n"); + if (node.isNodeDiagnosticsEnabled()) { + textBuilder.append(threadsStats()); + textBuilder.append("\n"); + } } this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index 58fa08a7459..265df08749a 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1105,6 +1105,8 @@ Node.enablePerNodeFailureTables=Enable per-node failure tables? Node.enablePerNodeFailureTablesLong=Enable automatically rerouting around nodes that failed a request within the last 10 minutes? Node.enableRoutedPing=Enable FNPRoutedPing? Node.enableRoutedPingLong=Enable FNPRoutedPing? Only useful in simulations, not on the real network. Turn it off. +Node.enableDiagnostics=Enable diagnostics? +Node.enableDiagnosticsLong=Enable diagnostics? Diagnostics keep track of thread CPU usage. It can be see in /diagnostics page. Node.enableSwapping=Enable location swapping? Node.enableSwappingLong=Enable location swapping? Node.enableSwapQueueing=Enable queueing of swap requests? diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 9f7167f7843..0bd7b2be2ad 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -754,6 +754,8 @@ public String[] getPossibleValues() { private boolean enableRoutedPing; + private boolean enableNodeDiagnostics; + private boolean peersOffersDismissed; /** @@ -2540,7 +2542,30 @@ public void set(Boolean val) throws InvalidConfigValueException, }); enableRoutedPing = nodeConfig.getBoolean("enableRoutedPing"); - + + nodeConfig.register("enableNodeDiagnostics", false, sortOrder++, true, false, "Node.enableDiagnostics", "Node.enableDiagnosticsLong", new BooleanCallback() { + @Override + public Boolean get() { + synchronized(Node.this) { + return enableNodeDiagnostics; + } + } + + @Override + public void set(Boolean val) throws InvalidConfigValueException, + NodeNeedRestartException { + synchronized(Node.this) { + enableNodeDiagnostics = val; + nodeDiagnostics.stop(); + + if (enableNodeDiagnostics) { + nodeDiagnostics.start(); + } + } + } + }); + enableNodeDiagnostics = nodeConfig.getBoolean("enableNodeDiagnostics"); + updateMTU(); // peers-offers/*.fref files @@ -3172,7 +3197,9 @@ public void start(boolean noSwaps) throws NodeInitException { // Process any data in the extra peer data directory peers.readExtraPeerData(); - nodeDiagnostics.start(); + if (enableNodeDiagnostics) { + nodeDiagnostics.start(); + } Logger.normal(this, "Started node"); @@ -4904,4 +4931,8 @@ DatabaseKey getDatabaseKey() { public NodeDiagnostics getNodeDiagnostics() { return nodeDiagnostics; } + + public boolean isNodeDiagnosticsEnabled() { + return enableNodeDiagnostics; + } } diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java index 09dca9961ac..a8f8f2b0339 100644 --- a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -32,6 +32,10 @@ public void start() { defaultThreadDiagnostics.start(); } + public void stop() { + defaultThreadDiagnostics.stop(); + } + /** * * @return List of threads registered in NodeStats.getThreads() diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 1a8a3019c13..a6eaf33346e 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -83,6 +83,10 @@ public void start() { scheduleNext(0); } + public void stop() { + ticker.removeQueuedJob(this); + } + private void scheduleNext() { scheduleNext(monitorInterval); } From c80d2dc64ea03cc3c709cb78c369838a36682f78 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:16:37 +0100 Subject: [PATCH 36/54] Avoid unnecessary casting to double for getCpuTimeDelta --- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 2 +- src/freenet/node/diagnostics/threads/NodeThreadInfo.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index a6eaf33346e..2ad52ed0c4f 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -102,7 +102,7 @@ private void scheduleNext() { * @param threadId Thread ID to get the CPU usage * @return Delta CPU time. */ - private double getCpuTimeDelta(long threadId) { + private long getCpuTimeDelta(long threadId) { long current = threadMxBean.getThreadCpuTime(threadId); long cpuUsage = current - threadCpu.getOrDefault(threadId, current); diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index 87f22920919..abfaf037e50 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -12,7 +12,7 @@ public class NodeThreadInfo { private final int prio; private final String groupName; private final String state; - private final double cpuTime; + private final long cpuTime; /** * @param id Thread ID @@ -22,7 +22,7 @@ public class NodeThreadInfo { * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) * @param cpuTime Thread's % of CPU time used */ - NodeThreadInfo(long id, String name, int prio, String groupName, String state, double cpuTime) { + NodeThreadInfo(long id, String name, int prio, String groupName, String state, long cpuTime) { this.id = id; this.name = name; this.prio = prio; @@ -51,7 +51,7 @@ public String getState() { return state; } - public double getCpuTime() { + public long getCpuTime() { return cpuTime; } } \ No newline at end of file From 89b6cfebaf9e17d5464841fa8ac4837a34377809 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:31:55 +0100 Subject: [PATCH 37/54] Declare interface rather than implementation --- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 2ad52ed0c4f..880584e5371 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -31,7 +31,7 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); /** Map to track thread's CPU differences between intervals of time */ - private final HashMap threadCpu = new HashMap<>(); + private final Map threadCpu = new HashMap<>(); /** * @param nodeStats Used to retrieve data points From 085c5d7fe37d304959a6a529ec2fbe8668d56514 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:35:00 +0100 Subject: [PATCH 38/54] Clean up nodeConfig callback --- src/freenet/node/Node.java | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 0bd7b2be2ad..045901d255d 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -2543,27 +2543,35 @@ public void set(Boolean val) throws InvalidConfigValueException, }); enableRoutedPing = nodeConfig.getBoolean("enableRoutedPing"); - nodeConfig.register("enableNodeDiagnostics", false, sortOrder++, true, false, "Node.enableDiagnostics", "Node.enableDiagnosticsLong", new BooleanCallback() { - @Override - public Boolean get() { - synchronized(Node.this) { - return enableNodeDiagnostics; + nodeConfig.register( + "enableNodeDiagnostics", + false, + sortOrder++, + true, + false, + "Node.enableDiagnostics", + "Node.enableDiagnosticsLong", + new BooleanCallback() { + @Override + public Boolean get() { + synchronized (Node.this) { + return enableNodeDiagnostics; + } } - } - @Override - public void set(Boolean val) throws InvalidConfigValueException, - NodeNeedRestartException { - synchronized(Node.this) { - enableNodeDiagnostics = val; - nodeDiagnostics.stop(); + @Override + public void set(Boolean val) { + synchronized (Node.this) { + enableNodeDiagnostics = val; + nodeDiagnostics.stop(); - if (enableNodeDiagnostics) { - nodeDiagnostics.start(); + if (enableNodeDiagnostics) { + nodeDiagnostics.start(); + } } } } - }); + ); enableNodeDiagnostics = nodeConfig.getBoolean("enableNodeDiagnostics"); updateMTU(); From 780b3625dc1ae604cb0d883dc271c28ea8c88f3d Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:44:57 +0100 Subject: [PATCH 39/54] Update configuration description --- src/freenet/l10n/freenet.l10n.en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index 265df08749a..48df7277b99 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1106,7 +1106,7 @@ Node.enablePerNodeFailureTablesLong=Enable automatically rerouting around nodes Node.enableRoutedPing=Enable FNPRoutedPing? Node.enableRoutedPingLong=Enable FNPRoutedPing? Only useful in simulations, not on the real network. Turn it off. Node.enableDiagnostics=Enable diagnostics? -Node.enableDiagnosticsLong=Enable diagnostics? Diagnostics keep track of thread CPU usage. It can be see in /diagnostics page. +Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner working (such as CPU usage per thread) which can enable the developers to better diagnose problems. The collected data is only stored in memory, it is not stored on your disk and it is not sent to anybody over the network. Node.enableSwapping=Enable location swapping? Node.enableSwappingLong=Enable location swapping? Node.enableSwapQueueing=Enable queueing of swap requests? From 030d225bd463b0d679fc7a2d46d9cc9c557b8214 Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:45:17 +0100 Subject: [PATCH 40/54] Check thread snapshot is available when displaying --- src/freenet/clients/http/DiagnosticToadlet.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index d5e02ff80dc..0febeb857df 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -429,6 +429,14 @@ private StringBuilder threadsStats() { .getThreadDiagnostics(); NodeThreadSnapshot threadSnapshot = threadDiagnostics.getThreadSnapshot(); + + // It's possible to request threadStats with out having taken any snapshot so far. + // In this scenario we'll just return a message indicating this. + if (threadSnapshot == null) { + sb.append("No threads registered."); + return sb; + } + double totalCpuTime = threadSnapshot.getTotalCpuTime(); List threads = threadSnapshot.getThreads(); From 612428a4ce0cb0f46bfa138c91efc92eb704548d Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:47:44 +0100 Subject: [PATCH 41/54] Simplify description and normalize names --- src/freenet/l10n/freenet.l10n.en.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index 48df7277b99..6b64bf428f5 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1105,8 +1105,8 @@ Node.enablePerNodeFailureTables=Enable per-node failure tables? Node.enablePerNodeFailureTablesLong=Enable automatically rerouting around nodes that failed a request within the last 10 minutes? Node.enableRoutedPing=Enable FNPRoutedPing? Node.enableRoutedPingLong=Enable FNPRoutedPing? Only useful in simulations, not on the real network. Turn it off. -Node.enableDiagnostics=Enable diagnostics? -Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner working (such as CPU usage per thread) which can enable the developers to better diagnose problems. The collected data is only stored in memory, it is not stored on your disk and it is not sent to anybody over the network. +Node.enableDiagnostics=Enable Diagnostics? +Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner working (such as CPU usage per thread) which can help diagnosing issues. The collected data is only stored in memory, it is not stored on your disk and it is not sent to anybody over the network. Node.enableSwapping=Enable location swapping? Node.enableSwappingLong=Enable location swapping? Node.enableSwapQueueing=Enable queueing of swap requests? From a11e880d281f089eb8ae6f13ef8ea098859068ff Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 21:52:13 +0100 Subject: [PATCH 42/54] Clean up unnecessary space --- src/freenet/node/diagnostics/DefaultNodeDiagnostics.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java index a8f8f2b0339..2f48012a668 100644 --- a/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java +++ b/src/freenet/node/diagnostics/DefaultNodeDiagnostics.java @@ -37,7 +37,6 @@ public void stop() { } /** - * * @return List of threads registered in NodeStats.getThreads() */ @Override From e5546628898716b6f86a640e354e53233bf8efac Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 15 May 2021 23:52:47 +0100 Subject: [PATCH 43/54] Correct language and simplify terms --- src/freenet/l10n/freenet.l10n.en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index 6b64bf428f5..9205866b0dd 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1106,7 +1106,7 @@ Node.enablePerNodeFailureTablesLong=Enable automatically rerouting around nodes Node.enableRoutedPing=Enable FNPRoutedPing? Node.enableRoutedPingLong=Enable FNPRoutedPing? Only useful in simulations, not on the real network. Turn it off. Node.enableDiagnostics=Enable Diagnostics? -Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner working (such as CPU usage per thread) which can help diagnosing issues. The collected data is only stored in memory, it is not stored on your disk and it is not sent to anybody over the network. +Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner-workings (such as CPU usage per thread) which can help to troubleshoot problems. The collected data is kept in memory (i.e. not persisted on disk) and it's not sent to anybody over the network. Node.enableSwapping=Enable location swapping? Node.enableSwappingLong=Enable location swapping? Node.enableSwapQueueing=Enable queueing of swap requests? From 7fc2794a3c5bb1dc7b54e2faa6fb6ce13a4a136e Mon Sep 17 00:00:00 2001 From: DC* Date: Mon, 17 May 2021 18:22:34 -0300 Subject: [PATCH 44/54] Fix grammar mistake on translation for DiagnosticsDescription --- src/freenet/l10n/freenet.l10n.en.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index 9205866b0dd..2e6ab831c1f 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1106,7 +1106,7 @@ Node.enablePerNodeFailureTablesLong=Enable automatically rerouting around nodes Node.enableRoutedPing=Enable FNPRoutedPing? Node.enableRoutedPingLong=Enable FNPRoutedPing? Only useful in simulations, not on the real network. Turn it off. Node.enableDiagnostics=Enable Diagnostics? -Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of it's inner-workings (such as CPU usage per thread) which can help to troubleshoot problems. The collected data is kept in memory (i.e. not persisted on disk) and it's not sent to anybody over the network. +Node.enableDiagnosticsLong=By enabling Diagnostics the node will keep detailed information of its inner-workings (such as CPU usage per thread) which can help to troubleshoot problems. The collected data is kept in memory (i.e. not persisted on disk) and it's not sent to anybody over the network. Node.enableSwapping=Enable location swapping? Node.enableSwappingLong=Enable location swapping? Node.enableSwapQueueing=Enable queueing of swap requests? From b64b8263ddbf6847d1706db759fc12301821251b Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 30 May 2021 03:59:05 +0100 Subject: [PATCH 45/54] Calculate CPU time as % of wall time --- src/freenet/clients/http/DiagnosticToadlet.java | 7 +++++-- .../node/diagnostics/threads/DefaultThreadDiagnostics.java | 2 +- src/freenet/node/diagnostics/threads/NodeThreadInfo.java | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 0febeb857df..80db6299fd8 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -7,6 +7,7 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.*; import freenet.client.HighLevelSimpleClient; @@ -437,7 +438,9 @@ private StringBuilder threadsStats() { return sb; } - double totalCpuTime = threadSnapshot.getTotalCpuTime(); + double wallTime = TimeUnit.MILLISECONDS.toNanos( + threadSnapshot.getInterval() + ); List threads = threadSnapshot.getThreads(); threads.sort(Comparator.comparing(NodeThreadInfo::getCpuTime).reversed()); @@ -465,7 +468,7 @@ private StringBuilder threadsStats() { thread.getPrio(), thread.getGroupName().substring(0, Math.min(10, thread.getGroupName().length())), thread.getState(), - thread.getCpuTime() / totalCpuTime * 100 + thread.getCpuTime() / wallTime * 100 ); sb.append(line); } diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 880584e5371..3d045aecda5 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -100,7 +100,7 @@ private void scheduleNext() { * will return 0. * * @param threadId Thread ID to get the CPU usage - * @return Delta CPU time. + * @return Delta CPU time (nanoseconds) */ private long getCpuTimeDelta(long threadId) { long current = threadMxBean.getThreadCpuTime(threadId); diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index abfaf037e50..b6e4ca09477 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -20,7 +20,7 @@ public class NodeThreadInfo { * @param prio Thread priority * @param groupName Thread's group name * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) - * @param cpuTime Thread's % of CPU time used + * @param cpuTime Thread's CPU time in nanoseconds (delta) */ NodeThreadInfo(long id, String name, int prio, String groupName, String state, long cpuTime) { this.id = id; From 62654e9817c3bc7637ee2093ce7ee0125917736a Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 24 Jun 2021 20:41:41 +0100 Subject: [PATCH 46/54] Avoid possible race condition on start up --- src/freenet/clients/http/DiagnosticToadlet.java | 7 ------- .../threads/DefaultThreadDiagnostics.java | 7 ++++++- .../diagnostics/threads/NodeThreadSnapshot.java | 17 +++-------------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 80db6299fd8..f4b524f1ca5 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -431,13 +431,6 @@ private StringBuilder threadsStats() { NodeThreadSnapshot threadSnapshot = threadDiagnostics.getThreadSnapshot(); - // It's possible to request threadStats with out having taken any snapshot so far. - // In this scenario we'll just return a message indicating this. - if (threadSnapshot == null) { - sb.append("No threads registered."); - return sb; - } - double wallTime = TimeUnit.MILLISECONDS.toNanos( threadSnapshot.getInterval() ); diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 3d045aecda5..c58795291a5 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -27,7 +27,12 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private static final int DEFAULT_MONITOR_INTERVAL = 1000; private static final String DEFAULT_MONITOR_THREAD_NAME = "NodeDiagnostics: thread monitor"; - private final AtomicReference nodeThreadSnapshot = new AtomicReference<>(); + /** Initialising with an empty NodeThreadSnapshot to avoid possible race conditions */ + private final AtomicReference nodeThreadSnapshot = + new AtomicReference<>( + new NodeThreadSnapshot(new ArrayList<>(), DEFAULT_MONITOR_INTERVAL) + ); + private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); /** Map to track thread's CPU differences between intervals of time */ diff --git a/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java b/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java index 22c5591d7a6..49158f27b43 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadSnapshot.java @@ -6,9 +6,11 @@ import java.util.ArrayList; import java.util.List; +/** + * Wrapper class to contain a list of NodeThreadInfos. + */ public class NodeThreadSnapshot { private final List threads; - private final double totalCpuTime; private final int interval; /** @@ -16,12 +18,6 @@ public class NodeThreadSnapshot { */ public NodeThreadSnapshot(List threads, int interval) { this.threads = new ArrayList<>(threads); - - totalCpuTime = Math.max(1, threads - .stream() - .mapToDouble(NodeThreadInfo::getCpuTime) - .sum()); - this.interval = interval; } @@ -32,13 +28,6 @@ public List getThreads() { return new ArrayList<>(threads); } - /** - * @return The calculated total CPU time from the snapshot's thread list. - */ - public double getTotalCpuTime() { - return totalCpuTime; - } - /** * @return Snapshot interval. */ From d33ccaad67100af66e3c9aab308e84a430fb7f39 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 24 Jun 2021 20:56:52 +0100 Subject: [PATCH 47/54] Add docblock to threadStats method --- src/freenet/clients/http/DiagnosticToadlet.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index f4b524f1ca5..e5d472dee66 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -422,6 +422,11 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { this.writeTextReply(ctx, 200, "OK", textBuilder.toString()); } + /** + * Retrieves ThreadDiagnostics (through NodeDiagnostics) to display + * thread information (id, name, group, % cpu, etc). + * @return Thread information in tab separated format. + */ private StringBuilder threadsStats() { StringBuilder sb = new StringBuilder(); From 405aa8d8efd5f6c04ce84da1e21185bfb2c2c135 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 24 Jun 2021 20:57:23 +0100 Subject: [PATCH 48/54] Fix tab vs space mix up --- src/freenet/node/Node.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/node/Node.java b/src/freenet/node/Node.java index 045901d255d..f85c70d7ccc 100644 --- a/src/freenet/node/Node.java +++ b/src/freenet/node/Node.java @@ -4936,9 +4936,9 @@ DatabaseKey getDatabaseKey() { return databaseKey; } - public NodeDiagnostics getNodeDiagnostics() { + public NodeDiagnostics getNodeDiagnostics() { return nodeDiagnostics; - } + } public boolean isNodeDiagnosticsEnabled() { return enableNodeDiagnostics; From 5cfa3c88c087c6a7571053c75e32df49561d92df Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 24 Jun 2021 21:02:42 +0100 Subject: [PATCH 49/54] Remove unneccessary code style fixes --- src/freenet/clients/http/DiagnosticToadlet.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index e5d472dee66..182eaf0dde9 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -6,7 +6,10 @@ import java.net.URI; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.*; @@ -177,7 +180,7 @@ public void handleMethodGET(URI uri, HTTPRequest request, ToadletContext ctx) th if ((numTransferringRequests == 0) && (numCHKRequests == 0) && (numSSKRequests == 0) && (numCHKInserts == 0) && (numSSKInserts == 0) && - (numTransferringRequestHandlers == 0) && + (numTransferringRequestHandlers == 0) && (numCHKOfferReplys == 0) && (numSSKOfferReplys == 0)) { textBuilder.append(l10n("noRequests")).append("\n"); } else { @@ -340,14 +343,14 @@ public int compare(PeerNodeStatus firstNode, PeerNodeStatus secondNode) { long totalBytesSentNodeToNode = node.nodeStats.getNodeToNodeBytesSent(); long totalBytesSentAllocationNotices = node.nodeStats.getAllocationNoticesBytesSent(); long totalBytesSentFOAF = node.nodeStats.getFOAFBytesSent(); - long totalBytesSentRemaining = total[0] - + long totalBytesSentRemaining = total[0] - (totalPayload + totalBytesSentCHKRequests + totalBytesSentSSKRequests + totalBytesSentCHKInserts + totalBytesSentSSKInserts + - totalBytesSentOfferedKeys + totalBytesSendOffers + totalBytesSentSwapOutput + + totalBytesSentOfferedKeys + totalBytesSendOffers + totalBytesSentSwapOutput + totalBytesSentAuth + totalBytesSentAckOnly + totalBytesSentResends + - totalBytesSentUOM + totalBytesSentAnnounce + + totalBytesSentUOM + totalBytesSentAnnounce + totalBytesSentRoutingStatus + totalBytesSentNetworkColoring + totalBytesSentPing + - totalBytesSentProbeRequest + totalBytesSentRouted + totalBytesSentDisconn + + totalBytesSentProbeRequest + totalBytesSentRouted + totalBytesSentDisconn + totalBytesSentInitial + totalBytesSentChangedIP + totalBytesSentNodeToNode + totalBytesSentAllocationNotices + totalBytesSentFOAF); textBuilder.append(l10n("requestOutput", new String[] { "chk", "ssk" }, new String[] { SizeUtil.formatSize(totalBytesSentCHKRequests, true), SizeUtil.formatSize(totalBytesSentSSKRequests, true) })).append("\n"); textBuilder.append(l10n("insertOutput", new String[] { "chk", "ssk" }, new String[] { SizeUtil.formatSize(totalBytesSentCHKInserts, true), SizeUtil.formatSize(totalBytesSentSSKInserts, true) })).append("\n"); @@ -522,4 +525,4 @@ private String l10n(String key, String[] patterns, String[] values) { public String path() { return TOADLET_URL; } -} \ No newline at end of file +} From 15ede8b1fb580b726249411bfa80bd95bc73d6c2 Mon Sep 17 00:00:00 2001 From: DC* Date: Thu, 24 Jun 2021 21:10:30 +0100 Subject: [PATCH 50/54] newline at end of line --- src/freenet/node/diagnostics/NodeDiagnostics.java | 2 +- src/freenet/node/diagnostics/threads/NodeThreadInfo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/freenet/node/diagnostics/NodeDiagnostics.java b/src/freenet/node/diagnostics/NodeDiagnostics.java index bba852959e8..114099d31a4 100644 --- a/src/freenet/node/diagnostics/NodeDiagnostics.java +++ b/src/freenet/node/diagnostics/NodeDiagnostics.java @@ -7,4 +7,4 @@ public interface NodeDiagnostics { ThreadDiagnostics getThreadDiagnostics(); -} \ No newline at end of file +} diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index b6e4ca09477..d62cfd9246b 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -54,4 +54,4 @@ public String getState() { public long getCpuTime() { return cpuTime; } -} \ No newline at end of file +} From ff72addf5ffaf64fb4947cd27d8730e8c85afcea Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 1 Aug 2021 04:21:51 +0100 Subject: [PATCH 51/54] Create threadSnapshot inner class to avoid pooler executor messing thread's names --- .../threads/DefaultThreadDiagnostics.java | 51 ++++++++++++++----- .../diagnostics/threads/NodeThreadInfo.java | 4 +- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index c58795291a5..62874526c2a 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -107,15 +107,40 @@ private void scheduleNext() { * @param threadId Thread ID to get the CPU usage * @return Delta CPU time (nanoseconds) */ - private long getCpuTimeDelta(long threadId) { + private long getCpuTimeDelta(long threadId, String name) { long current = threadMxBean.getThreadCpuTime(threadId); long cpuUsage = current - threadCpu.getOrDefault(threadId, current); - threadCpu.put(threadId, current); + threadSnapshot.put(threadId, new ThreadSnapshot(current, name)); return cpuUsage; } + private final Map threadSnapshot = new HashMap<>(); + + /** + * Class holder for cpu and thread name at the moment of measurement. This is + * necessary as the threads are pooled and may change name right after measurement. + */ + private class ThreadSnapshot { + private final long cpu; + private final String name; + + public ThreadSnapshot(long cpu, String name) { + this.cpu = cpu; + this.name = name; + } + + public String getName() { + return name; + } + + public long getCpu() { + return cpu; + } + } + + /** * Remove threads that aren't present in the last snapshot. * @@ -133,18 +158,18 @@ private void purgeInactiveThreads(List threads) { @Override public void run() { List threads = Arrays.stream(nodeStats.getThreads()) - .filter(Objects::nonNull) - .filter(thread -> thread.getThreadGroup() != null) - .map(thread -> new NodeThreadInfo( - thread.getId(), - thread.getName(), - thread.getPriority(), - thread.getThreadGroup().getName(), - thread.getState().toString(), - getCpuTimeDelta(thread.getId()) + .filter(Objects::nonNull) + .filter(thread -> thread.getThreadGroup() != null) + .map(thread -> new NodeThreadInfo( + thread.getId(), + getCpuTimeDelta(thread.getId(), thread.getName()), + threadSnapshot.get(thread.getId()).getName(), + thread.getPriority(), + thread.getThreadGroup().getName(), + thread.getState().toString() + ) ) - ) - .collect(Collectors.toList()); + .collect(Collectors.toList()); nodeThreadSnapshot.set( new NodeThreadSnapshot(threads, monitorInterval) diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index d62cfd9246b..c518f228e4d 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -16,13 +16,13 @@ public class NodeThreadInfo { /** * @param id Thread ID + * @param cpuTime Thread's CPU time in nanoseconds (delta) * @param name Thread name, or * @param prio Thread priority * @param groupName Thread's group name * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) - * @param cpuTime Thread's CPU time in nanoseconds (delta) */ - NodeThreadInfo(long id, String name, int prio, String groupName, String state, long cpuTime) { + NodeThreadInfo(long id, long cpuTime, String name, int prio, String groupName, String state) { this.id = id; this.name = name; this.prio = prio; From d1660b931a76cffe32e6cf0e13be2d22fc42cc1a Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 1 Aug 2021 05:10:08 +0100 Subject: [PATCH 52/54] Replace threadCpu for threadSnapshot --- .../diagnostics/threads/DefaultThreadDiagnostics.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 62874526c2a..12f0b0a327f 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -36,7 +36,7 @@ public class DefaultThreadDiagnostics implements Runnable, ThreadDiagnostics { private final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean(); /** Map to track thread's CPU differences between intervals of time */ - private final Map threadCpu = new HashMap<>(); + private final Map threadSnapshot = new HashMap<>(); /** * @param nodeStats Used to retrieve data points @@ -110,14 +110,13 @@ private void scheduleNext() { private long getCpuTimeDelta(long threadId, String name) { long current = threadMxBean.getThreadCpuTime(threadId); - long cpuUsage = current - threadCpu.getOrDefault(threadId, current); + ThreadSnapshot snapshot = threadSnapshot.get(threadId); + long cpuUsage = current - (snapshot != null ? snapshot.getCpu() : 0); threadSnapshot.put(threadId, new ThreadSnapshot(current, name)); return cpuUsage; } - private final Map threadSnapshot = new HashMap<>(); - /** * Class holder for cpu and thread name at the moment of measurement. This is * necessary as the threads are pooled and may change name right after measurement. @@ -151,7 +150,7 @@ private void purgeInactiveThreads(List threads) { .map(NodeThreadInfo::getId) .collect(Collectors.toList()); - threadCpu.keySet() + threadSnapshot.keySet() .removeIf(key -> !activeThreads.contains(key)); } From 65de06b87c023f9baa76ad76348e0f9645a41b84 Mon Sep 17 00:00:00 2001 From: DC* Date: Sun, 1 Aug 2021 11:38:48 +0100 Subject: [PATCH 53/54] Use thread synchronization to avoid misnaming --- .../threads/DefaultThreadDiagnostics.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index 12f0b0a327f..e8a605c8f13 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -104,11 +104,18 @@ private void scheduleNext() { * If there's no previous snapshot of CPU Time for the thread this method * will return 0. * - * @param threadId Thread ID to get the CPU usage + * @param thread Thread object to get the CPU usage * @return Delta CPU time (nanoseconds) */ - private long getCpuTimeDelta(long threadId, String name) { - long current = threadMxBean.getThreadCpuTime(threadId); + private long getCpuTimeDelta(Thread thread) { + long threadId = thread.getId(), current; + String name; + // Synchronizing thread to avoid PoolerExecutor to change thread name + // while we're measuring it. + synchronized (thread) { + name = thread.getName(); + current = threadMxBean.getThreadCpuTime(threadId); + } ThreadSnapshot snapshot = threadSnapshot.get(threadId); long cpuUsage = current - (snapshot != null ? snapshot.getCpu() : 0); @@ -121,7 +128,7 @@ private long getCpuTimeDelta(long threadId, String name) { * Class holder for cpu and thread name at the moment of measurement. This is * necessary as the threads are pooled and may change name right after measurement. */ - private class ThreadSnapshot { + private static class ThreadSnapshot { private final long cpu; private final String name; @@ -161,7 +168,7 @@ public void run() { .filter(thread -> thread.getThreadGroup() != null) .map(thread -> new NodeThreadInfo( thread.getId(), - getCpuTimeDelta(thread.getId(), thread.getName()), + getCpuTimeDelta(thread), threadSnapshot.get(thread.getId()).getName(), thread.getPriority(), thread.getThreadGroup().getName(), From 456d9c2cb68bd42dc7e478b71276e3fd4ed125bd Mon Sep 17 00:00:00 2001 From: DC* Date: Sat, 14 Aug 2021 14:02:04 +0100 Subject: [PATCH 54/54] Use job ID to keep track of CPU usage --- .../clients/http/DiagnosticToadlet.java | 12 +++--- .../threads/DefaultThreadDiagnostics.java | 42 ++++++++++++++++--- .../diagnostics/threads/NodeThreadInfo.java | 9 +++- src/freenet/support/PooledExecutor.java | 16 +++++-- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/freenet/clients/http/DiagnosticToadlet.java b/src/freenet/clients/http/DiagnosticToadlet.java index 182eaf0dde9..a44a33d54bb 100644 --- a/src/freenet/clients/http/DiagnosticToadlet.java +++ b/src/freenet/clients/http/DiagnosticToadlet.java @@ -448,11 +448,12 @@ private StringBuilder threadsStats() { sb.append(String.format("Threads (%d):%n", threads.size())); - // ID, Name, Priority, Group (system, main), Status, % CPU + // Thread ID, Job ID, Name, Priority, Group (system, main), Status, % CPU sb.append( String.format( - "%5s %-60s %5s %10s %-20s %-5s%n", - "ID", + "%10s %15s %-90s %5s %10s %-20s %-5s%n", + "Thread ID", + "Job ID", "Name", "Prio.", "Group", @@ -463,9 +464,10 @@ private StringBuilder threadsStats() { for (NodeThreadInfo thread : threads) { String line = String.format( - "%5s %-60s %5s %10s %-20s %.2f%n", + "%10s %15s %-90s %5s %10s %-20s %.2f%n", thread.getId(), - thread.getName().substring(0, Math.min(60, thread.getName().length())), + thread.getJobId(), + thread.getName().substring(0, Math.min(90, thread.getName().length())), thread.getPrio(), thread.getGroupName().substring(0, Math.min(10, thread.getGroupName().length())), thread.getState(), diff --git a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java index e8a605c8f13..302b43bd98c 100644 --- a/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java +++ b/src/freenet/node/diagnostics/threads/DefaultThreadDiagnostics.java @@ -7,6 +7,7 @@ import freenet.node.diagnostics.*; import freenet.support.*; +import java.io.*; import java.lang.management.*; import java.util.*; import java.util.concurrent.atomic.*; @@ -108,22 +109,38 @@ private void scheduleNext() { * @return Delta CPU time (nanoseconds) */ private long getCpuTimeDelta(Thread thread) { - long threadId = thread.getId(), current; + long jobId, current; String name; // Synchronizing thread to avoid PoolerExecutor to change thread name // while we're measuring it. synchronized (thread) { name = thread.getName(); - current = threadMxBean.getThreadCpuTime(threadId); + current = threadMxBean.getThreadCpuTime(thread.getId()); + jobId = getJobId(thread); } - ThreadSnapshot snapshot = threadSnapshot.get(threadId); + ThreadSnapshot snapshot = threadSnapshot.get(jobId); long cpuUsage = current - (snapshot != null ? snapshot.getCpu() : 0); - threadSnapshot.put(threadId, new ThreadSnapshot(current, name)); + threadSnapshot.put(jobId, new ThreadSnapshot(current, name)); return cpuUsage; } + /** + * Gets the job's ID from the thread (PooledExecutor.MyThread) or + * defaults to the thread's ID. + * @param thread + * @return Job ID or Thread ID. + */ + private long getJobId(Thread thread) { + long jobId = thread.getId(); + if ((thread instanceof PooledExecutor.MyThread)) { + jobId = ((PooledExecutor.MyThread) thread).getJobId(); + } + + return jobId; + } + /** * Class holder for cpu and thread name at the moment of measurement. This is * necessary as the threads are pooled and may change name right after measurement. @@ -154,22 +171,35 @@ public long getCpu() { */ private void purgeInactiveThreads(List threads) { List activeThreads = threads.stream() - .map(NodeThreadInfo::getId) + .map(NodeThreadInfo::getJobId) // job id might be the same as thread id .collect(Collectors.toList()); threadSnapshot.keySet() .removeIf(key -> !activeThreads.contains(key)); } + /** + * Get thread (jobs) name at the time of measurement or default + * to current's thread name. + * @param thread + * @return Thread's name + */ + private String getJobName(Thread thread) { + ThreadSnapshot ts = threadSnapshot.get(getJobId(thread)); + return ts != null ? ts.getName() : thread.getName(); + } + @Override public void run() { List threads = Arrays.stream(nodeStats.getThreads()) .filter(Objects::nonNull) .filter(thread -> thread.getThreadGroup() != null) + .filter(thread -> getJobId(thread) != 0) .map(thread -> new NodeThreadInfo( thread.getId(), + getJobId(thread), getCpuTimeDelta(thread), - threadSnapshot.get(thread.getId()).getName(), + getJobName(thread), thread.getPriority(), thread.getThreadGroup().getName(), thread.getState().toString() diff --git a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java index c518f228e4d..bd06567272e 100644 --- a/src/freenet/node/diagnostics/threads/NodeThreadInfo.java +++ b/src/freenet/node/diagnostics/threads/NodeThreadInfo.java @@ -8,6 +8,7 @@ */ public class NodeThreadInfo { private final long id; + private final long jobId; private final String name; private final int prio; private final String groupName; @@ -16,14 +17,16 @@ public class NodeThreadInfo { /** * @param id Thread ID + * @param jobId Job ID * @param cpuTime Thread's CPU time in nanoseconds (delta) * @param name Thread name, or * @param prio Thread priority * @param groupName Thread's group name * @param state Thread current state (TIMED_WAITING, RUNNABLE, etc) */ - NodeThreadInfo(long id, long cpuTime, String name, int prio, String groupName, String state) { + NodeThreadInfo(long id, long jobId, long cpuTime, String name, int prio, String groupName, String state) { this.id = id; + this.jobId = jobId; this.name = name; this.prio = prio; this.groupName = groupName; @@ -35,6 +38,10 @@ public long getId() { return id; } + public long getJobId() { + return jobId; + } + public String getName() { return name; } diff --git a/src/freenet/support/PooledExecutor.java b/src/freenet/support/PooledExecutor.java index 4891136b245..cf9e2dd0f3c 100644 --- a/src/freenet/support/PooledExecutor.java +++ b/src/freenet/support/PooledExecutor.java @@ -6,6 +6,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import java.util.ArrayList; +import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import freenet.node.PrioRunnable; @@ -161,17 +162,24 @@ public int getWaitingThreadsCount() { private static class Job { private final Runnable runnable; private final String name; + private final int id; Job(Runnable runnable, String name) { this.runnable = runnable; this.name = name; + this.id = new Random().nextInt(); + } + + public int getId() { + return id; } } - private class MyThread extends NativeThread { + public class MyThread extends NativeThread { final String defaultName; volatile boolean alive = true; Job nextJob; + Job job; final long threadNo; private boolean removed = false; @@ -195,12 +203,14 @@ public void realRun() { } } } + + public int getJobId() { + return job != null ? job.id : nextJob != null ? nextJob.id : 0; + } private void innerRun(int nativePriority) { long ranJobs = 0; while(true) { - Job job; - synchronized(this) { job = nextJob; nextJob = null;