org.graalvm.visualvm.sampler.cpu.CCTFlattener Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org-graalvm-visualvm-sampler Show documentation
Show all versions of org-graalvm-visualvm-sampler Show documentation
This module implements a sampling CPU and memory profiler (in contrast to the Profiler which uses instrumentation).<BR><BR>
CPU profiling requires a JMX connection to the monitored application, once correctly set up you can profile both local and remote applications.<BR><BR>
Memory profiling requires Attach API, so only local applications running on JDK 6+ are supported.
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.visualvm.sampler.cpu;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import org.graalvm.visualvm.lib.jfluid.filters.InstrumentationFilter;
import org.graalvm.visualvm.lib.jfluid.results.RuntimeCCTNodeProcessor;
import org.graalvm.visualvm.lib.jfluid.results.cpu.FlatProfileContainer;
import org.graalvm.visualvm.lib.jfluid.results.cpu.MethodInfoMapper;
import org.graalvm.visualvm.lib.jfluid.results.cpu.cct.nodes.MethodCPUCCTNode;
import org.graalvm.visualvm.lib.jfluid.results.cpu.cct.nodes.TimedCPUCCTNode;
/**
*
* @author Tomas Hurka
*/
final class CCTFlattener extends RuntimeCCTNodeProcessor.PluginAdapter {
//~ Instance fields ----------------------------------------------------------------------------------------------------------
private final Object containerGuard = new Object();
// @GuardedBy containerGuard
private FlatProfileContainer container;
private Stack parentStack;
private Set methodsOnStack;
private int[] invDiff;
private int[] invPM;
private int[] nCalleeInvocations;
private long[] timePM0;
private long[] timePM1;
private long[] totalTimePM0;
private long[] totalTimePM1;
private int nMethods;
private InstrumentationFilter instrFilter;
private boolean twoTimestamps;
private MethodInfoMapper methodInfoMapper;
//~ Constructors -------------------------------------------------------------------------------------------------------------
CCTFlattener(boolean twoStamps, MethodInfoMapper mapper, InstrumentationFilter f) {
parentStack = new Stack<>();
methodsOnStack = new HashSet<>();
nMethods = mapper.getMaxMethodId();
methodInfoMapper = mapper;
twoTimestamps = twoStamps;
instrFilter = f;
}
//~ Methods ------------------------------------------------------------------------------------------------------------------
FlatProfileContainer getFlatProfile() {
synchronized (containerGuard) {
return container;
}
}
public void onStop() {
// Now convert the data into microseconds
long wholeGraphTime0 = 0;
// Now convert the data into microseconds
long wholeGraphTime1 = 0;
long totalNInv = 0;
for (int i = 0; i < nMethods; i++) {
// convert to microseconds
double time = timePM0[i] / 1000.0;
if (time < 0) {
// in some cases the combination of cleansing the time by calibration and subtracting wait/sleep
// times can lead to <0 time
// see https://netbeans.org/bugzilla/show_bug.cgi?id=64416
time = 0;
}
timePM0[i] = (long) time;
// don't include the Thread time into wholegraphtime
if (i > 0) {
wholeGraphTime0 += time;
}
if (twoTimestamps) {
// convert to microseconds
time = timePM1[i] / 1000.0;
timePM1[i] = (long) time;
// don't include the Thread time into wholegraphtime
if (i > 0) {
wholeGraphTime1 += time;
}
}
totalNInv += invPM[i];
}
synchronized (containerGuard) {
container = new FlatProfilerContainer(methodInfoMapper, twoTimestamps, timePM0, timePM1, totalTimePM0, totalTimePM1,
invPM, new char[0], wholeGraphTime0, wholeGraphTime1, invPM.length);
}
timePM0 = timePM1 = null;
invPM = invDiff = nCalleeInvocations = null;
parentStack.clear();
methodsOnStack.clear();
instrFilter = null;
}
public void onStart() {
timePM0 = new long[nMethods];
timePM1 = new long[twoTimestamps ? nMethods : 0];
totalTimePM0 = new long[nMethods];
totalTimePM1 = new long[twoTimestamps ? nMethods : 0];
invPM = new int[nMethods];
invDiff = new int[nMethods];
nCalleeInvocations = new int[nMethods];
parentStack.clear();
methodsOnStack.clear();
synchronized (containerGuard) {
container = null;
}
}
public void onNode(MethodCPUCCTNode node) {
final int nodeMethodId = node.getMethodId();
final int nodeFilerStatus = node.getFilteredStatus();
final MethodCPUCCTNode currentParent = parentStack.isEmpty() ? null : parentStack.peek().parent;
boolean filteredOut = (nodeFilerStatus == TimedCPUCCTNode.FILTERED_YES); // filtered out by rootmethod/markermethod rules
if (!filteredOut) {
String jvmClassName = methodInfoMapper.getInstrMethodClass(nodeMethodId).replace('.', '/'); // NOI18N
filteredOut = !instrFilter.passes(jvmClassName);
}
final int parentMethodId = currentParent != null ? currentParent.getMethodId() : -1;
if (filteredOut) {
if ((currentParent != null) && !currentParent.isRoot()) {
invDiff[parentMethodId] += node.getNCalls();
timePM0[parentMethodId] += node.getNetTime0();
if (twoTimestamps) {
timePM1[parentMethodId] += node.getNetTime1();
}
}
} else {
timePM0[nodeMethodId] += node.getNetTime0();
if (twoTimestamps) {
timePM1[nodeMethodId] += node.getNetTime1();
}
invPM[nodeMethodId] += node.getNCalls();
if ((currentParent != null) && !currentParent.isRoot()) {
nCalleeInvocations[parentMethodId] += node.getNCalls();
}
}
final MethodCPUCCTNode nextParent = filteredOut ? currentParent : node;
final TotalTime timeNode = new TotalTime(nextParent,methodsOnStack.contains(nodeMethodId));
timeNode.totalTimePM0+=node.getNetTime0();
if (twoTimestamps) timeNode.totalTimePM1+=node.getNetTime1();
if (!timeNode.recursive) {
methodsOnStack.add(nodeMethodId);
}
parentStack.push(timeNode);
}
public void onBackout(MethodCPUCCTNode node) {
TotalTime current = parentStack.pop();
if (!current.recursive) {
int nodeMethodId = node.getMethodId();
methodsOnStack.remove(nodeMethodId);
// convert to microseconds
double time = current.totalTimePM0 / 1000.0;
if (time>0) {
totalTimePM0[nodeMethodId]+=time;
}
if (twoTimestamps) {
time = current.totalTimePM1 / 1000.0;
if (time>0) {
totalTimePM1[nodeMethodId]+=time;
}
}
}
// add self data to parent
if (!parentStack.isEmpty()) {
TotalTime parent = parentStack.peek();
parent.add(current);
parent.outCalls+=node.getNCalls();
}
}
private static class TotalTime {
private final MethodCPUCCTNode parent;
private final boolean recursive;
private int outCalls;
private long totalTimePM0;
private long totalTimePM1;
TotalTime(MethodCPUCCTNode n, boolean r) {
parent = n;
recursive = r;
}
private void add(TotalTime current) {
outCalls += current.outCalls;
totalTimePM0 += current.totalTimePM0;
totalTimePM1 += current.totalTimePM1;
}
}
}