Skip to content

Commit

Permalink
golang: add heap analysis
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Khouzam <[email protected]>
Change-Id: Iae1533332f04ba289f0a9c7246363ac687331760
  • Loading branch information
MatthewKhouzam committed Oct 24, 2024
1 parent 69920b5 commit 417086e
Show file tree
Hide file tree
Showing 12 changed files with 639 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
Expand All @@ -30,5 +35,6 @@
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.tracecompass.common.core,
org.eclipse.tracecompass.tmf.ui,
org.eclipse.tracecompass.tmf.core,
org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional,
org.eclipse.tracecompass.analysis.os.linux.core
Export-Package: org.eclipse.tracecompass.incubator.internal.golang.core;x-friends:="org.eclipse.tracecompass.incubator.golang.core.tests",
org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory,
org.eclipse.tracecompass.incubator.internal.golang.core.trace
Automatic-Module-Name: org.eclipse.tracecompass.incubator.golang.core
Import-Package: com.google.common.collect
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,48 @@
trace_type="org.eclipse.tracecompass.incubator.internal.golang.core.trace.GolangTrace">
</type>
</extension>
<extension
point="org.eclipse.linuxtools.tmf.core.analysis">
<module
analysis_module="org.eclipse.tracecompass.internal.tmf.core.analysis.callsite.CallsiteAnalysis"
automatic="true"
id="org.eclipse.tracecompass.incubator.golang.core.callsite"
name="Callsite">
<tracetype
applies="true"
class="org.eclipse.tracecompass.incubator.internal.golang.core.trace.GolangTrace">
</tracetype>
</module>
<output
class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
id="org.eclipse.tracecompass.incubator.golang.core.callsite">
<analysisId
id="org.eclipse.tracecompass.incubator.golang.core.callsite">
</analysisId>
</output>
<module
analysis_module="org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory.GoMemoryAnalysisModule"
id="org.eclipse.tracecompass.analysis.golang.memory.analysis"
name="Memory Analysis">
<tracetype
applies="true"
class="org.eclipse.tracecompass.incubator.internal.golang.core.trace.GolangTrace">
</tracetype>
</module>
<output
class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
id="org.eclipse.tracecompass.incubator.golang.ui.memoryUsage">
<analysisId
id="org.eclipse.tracecompass.analysis.golang.memory.analysis">
</analysisId>
</output>
</extension>
<extension
point="org.eclipse.tracecompass.tmf.core.dataprovider">
<dataProviderFactory
class="org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory.GoMemoryDataProviderFactory"
id="org.eclipse.tracecompass.analysis.golang.memory.analysis">
</dataProviderFactory>
</extension>

</plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**********************************************************************
* Copyright (c) 2016 École Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory;

import java.util.Objects;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;


/**
* This analysis module creates a stateprovider that keeps track of the memory
* allocated and deallocated by the kernel
*
* @author Samuel Gagnon
* @since 2.0
*/
public class GoMemoryAnalysisModule extends TmfStateSystemAnalysisModule {

/**
* Analysis ID, it should match that in the plugin.xml file
*/
public static final @NonNull String ID = "org.eclipse.tracecompass.analysis.golang.memory.analysis"; //$NON-NLS-1$

/**
* Each thread attribute in the tree has an attribute for keeping the lowest memory
* value for that thread during the trace. (Those values can be negative because we
* don't have access to a memory dump before the trace)
*/
public static final @NonNull String THREAD_LOWEST_MEMORY_VALUE = "lowestMemory"; //$NON-NLS-1$

@Override
protected @NonNull ITmfStateProvider createStateProvider() {
return new GoMemoryStateProvider(Objects.requireNonNull(getTrace()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**********************************************************************
* Copyright (c) 2017, 2018 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

package org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.function.Predicate;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor.ProviderType;
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderFactory;
import org.eclipse.tracecompass.tmf.core.model.DataProviderDescriptor;
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
import org.eclipse.tracecompass.tmf.core.model.xy.TmfTreeXYCompositeDataProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;


/**
* Factory to create instances of the {@link GoMemoryUsageDataProvider}.
* Uses the DataProviderFactory extension point.
*
* @since 2.4
* @author Loic Prieur-Drevon
*/
public class GoMemoryDataProviderFactory implements IDataProviderFactory {

private static final @NonNull String TITLE = Objects.requireNonNull("Heap Usage");
private static final Predicate<? super ITmfTrace> PREDICATE = t -> TmfTraceUtils.getAnalysisModuleOfClass(t, GoMemoryAnalysisModule.class, GoMemoryAnalysisModule.ID) != null;
private static final IDataProviderDescriptor DESCRIPTOR = new DataProviderDescriptor.Builder()
.setId(GoMemoryUsageDataProvider.ID)
.setName(TITLE)
.setDescription(Objects.requireNonNull("Heap Usage"))
.setProviderType(ProviderType.TREE_TIME_XY)
.build();

@Override
public @Nullable ITmfTreeDataProvider<? extends ITmfTreeDataModel> createProvider(@NonNull ITmfTrace trace) {
Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
if (traces.size() == 1) {
return GoMemoryUsageDataProvider.create(trace);

}
return TmfTreeXYCompositeDataProvider.create(traces, TITLE, GoMemoryUsageDataProvider.ID);
}

@Override
public Collection<IDataProviderDescriptor> getDescriptors(@NonNull ITmfTrace trace) {
Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(trace);
return traces.stream().anyMatch(PREDICATE) ? Collections.singletonList(DESCRIPTOR) : Collections.emptyList();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**********************************************************************
* Copyright (c) 2016 Polytechnique de Montréal
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.incubator.internal.golang.core.analysis.memory;

import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;

/**
* Creates a state system and computes the total memory usage for all threads
* and for each selected thread from a kernel trace. It examines the page
* allocation and deallocation events in the kernel to do so.
*
* The state provider also contains code that can query the state system.
*
* Attribute tree:
*
* <pre>
* |- <TID number> -> current memory usage
* | |- THREAD_LOWEST_MEMORY_VALUE -> lowest memory value for thread
* </pre>
*
* @author Samuel Gagnon
* @since 2.0
*/
public class GoMemoryStateProvider extends AbstractTmfStateProvider {

/**
* Special string to save memory allocation when tid is not known
*/
public static final String OTHER_TID = "other"; //$NON-NLS-1$

/* Version of this state provider */
private static final int VERSION = 1;

/**
* Constructor
*
* @param trace
* trace
* @param layout
* layout
*/
public GoMemoryStateProvider(@NonNull ITmfTrace trace) {
super(trace, "go:Memory"); //$NON-NLS-1$

}

@Override
public int getVersion() {
return VERSION;
}

@Override
public ITmfStateProvider getNewInstance() {
return new GoMemoryStateProvider(getTrace());
}

@Override
protected void eventHandle(@NonNull ITmfEvent event) {
String name = event.getName();

long inc;
if (name.equals("HeapAlloc")) {
Long fieldValue = event.getContent().getFieldValue(Long.class, "mem");
if (fieldValue == null ) {
return;
}
inc = fieldValue;
} else if (name.contains("GC")) {
Long fieldValue = event.getContent().getFieldValue(Long.class, "mem");
if (fieldValue == null ) {
return;
}
inc = -fieldValue;
} else {
return;
}

ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
long ts = event.getTimestamp().toNanos();

Long tidField =event.getContent().getFieldValue(Long.class,"g");
String tid;
if (tidField == null) {
// if the TID is not available
tid = OTHER_TID;
} else {
tid = tidField.toString();
}

int tidQuark = ss.getQuarkAbsoluteAndAdd(tid);
StateSystemBuilderUtils.incrementAttributeLong(ss, ts, tidQuark, inc);
long currentMemoryValue = ss.queryOngoingState(tidQuark).unboxLong();

/**
* We add an attribute to keep the lowest memory value for each thread. This
* quantity is used when we plot to avoid negative values.
*/
int lowestMemoryQuark = ss.getQuarkRelativeAndAdd(tidQuark, GoMemoryAnalysisModule.THREAD_LOWEST_MEMORY_VALUE);
ITmfStateValue lowestMemoryValue = ss.queryOngoingState(lowestMemoryQuark);
long previousLowest = lowestMemoryValue.isNull() ? 0 : lowestMemoryValue.unboxLong();

if (previousLowest > currentMemoryValue) {
ss.modifyAttribute(ts, currentMemoryValue, lowestMemoryQuark);
}
}
}
Loading

0 comments on commit 417086e

Please sign in to comment.