Skip to content

Commit

Permalink
Refactor sampler node export process
Browse files Browse the repository at this point in the history
  • Loading branch information
lucko committed Sep 3, 2024
1 parent 35b557a commit 684cc5e
Show file tree
Hide file tree
Showing 19 changed files with 490 additions and 264 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@
import me.lucko.spark.common.sampler.ThreadDumper;
import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.async.AsyncSampler;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.java.MergeStrategy;
import me.lucko.spark.common.sampler.source.ClassSourceLookup;
import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.common.util.FormatUtil;
import me.lucko.spark.common.util.MediaTypes;
import me.lucko.spark.common.util.MethodDisambiguator;
import me.lucko.spark.common.ws.ViewerSocket;
import me.lucko.spark.proto.SparkSamplerProtos;
import net.kyori.adventure.text.Component;
Expand Down Expand Up @@ -507,12 +506,7 @@ private Sampler.ExportProps getExportProps(SparkPlatform platform, CommandRespon
return new Sampler.ExportProps()
.creator(resp.senderData())
.comment(Iterables.getFirst(arguments.stringFlag("comment"), null))
.mergeMode(() -> {
MethodDisambiguator methodDisambiguator = new MethodDisambiguator(platform.createClassFinder());
return arguments.boolFlag("separate-parent-calls")
? MergeMode.separateParentCalls(methodDisambiguator)
: MergeMode.sameMethod(methodDisambiguator);
})
.mergeStrategy(arguments.boolFlag("separate-parent-calls") ? MergeStrategy.SEPARATE_PARENT_CALLS : MergeStrategy.SAME_METHOD)
.classSourceLookup(() -> ClassSourceLookup.create(platform));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
import me.lucko.spark.common.monitor.memory.GarbageCollectorStatistics;
import me.lucko.spark.common.platform.SparkMetadata;
import me.lucko.spark.common.sampler.aggregator.DataAggregator;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.node.ThreadNode;
import me.lucko.spark.common.sampler.node.exporter.NodeExporter;
import me.lucko.spark.common.sampler.source.ClassSourceLookup;
import me.lucko.spark.common.sampler.window.ProtoTimeEncoder;
import me.lucko.spark.common.sampler.window.WindowStatisticsCollector;
Expand All @@ -42,6 +42,7 @@
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Supplier;

/**
Expand Down Expand Up @@ -198,7 +199,7 @@ protected void writeMetadataToProto(SamplerData.Builder proto, SparkPlatform pla
proto.setMetadata(metadata);
}

protected void writeDataToProto(SamplerData.Builder proto, DataAggregator dataAggregator, MergeMode mergeMode, ClassSourceLookup classSourceLookup, Supplier<ClassFinder> classFinderSupplier) {
protected void writeDataToProto(SamplerData.Builder proto, DataAggregator dataAggregator, Function<ProtoTimeEncoder, NodeExporter> nodeExporterFunction, ClassSourceLookup classSourceLookup, Supplier<ClassFinder> classFinderSupplier) {
List<ThreadNode> data = dataAggregator.exportData();
data.sort(Comparator.comparing(ThreadNode::getThreadLabel));

Expand All @@ -213,8 +214,10 @@ protected void writeDataToProto(SamplerData.Builder proto, DataAggregator dataAg
this.windowStatisticsCollector.ensureHasStatisticsForAllWindows(timeWindows);
proto.putAllTimeWindowStatistics(this.windowStatisticsCollector.export());

NodeExporter exporter = nodeExporterFunction.apply(timeEncoder);

for (ThreadNode entry : data) {
proto.addThreads(entry.toProto(mergeMode, timeEncoder));
proto.addThreads(exporter.export(entry));
classSourceVisitor.visit(entry);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import me.lucko.spark.common.SparkPlatform;
import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.java.MergeStrategy;
import me.lucko.spark.common.sampler.source.ClassSourceLookup;
import me.lucko.spark.common.ws.ViewerSocket;
import me.lucko.spark.proto.SparkProtos;
Expand Down Expand Up @@ -111,7 +111,7 @@ public interface Sampler {
final class ExportProps {
private CommandSender.Data creator;
private String comment;
private Supplier<MergeMode> mergeMode;
private MergeStrategy mergeStrategy;
private Supplier<ClassSourceLookup> classSourceLookup;
private SocketChannelInfo channelInfo;

Expand All @@ -126,8 +126,8 @@ public String comment() {
return this.comment;
}

public Supplier<MergeMode> mergeMode() {
return this.mergeMode;
public MergeStrategy mergeStrategy() {
return this.mergeStrategy;
}

public Supplier<ClassSourceLookup> classSourceLookup() {
Expand All @@ -148,8 +148,8 @@ public ExportProps comment(String comment) {
return this;
}

public ExportProps mergeMode(Supplier<MergeMode> mergeMode) {
this.mergeMode = mergeMode;
public ExportProps mergeStrategy(MergeStrategy mergeStrategy) {
this.mergeStrategy = mergeStrategy;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class AsyncDataAggregator extends AbstractDataAggregator {

/** A describer for async-profiler stack trace elements. */
private static final StackTraceNode.Describer<AsyncStackTraceElement> STACK_TRACE_DESCRIBER = (element, parent) ->
new StackTraceNode.Description(element.getClassName(), element.getMethodName(), element.getMethodDescription());
new StackTraceNode.AsyncDescription(element.getClassName(), element.getMethodName(), element.getMethodDescription());

protected AsyncDataAggregator(ThreadGrouper threadGrouper) {
super(threadGrouper);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* This file is part of spark.
*
* Copyright (c) lucko (Luck) <[email protected]>
* Copyright (c) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.sampler.async;

import me.lucko.spark.common.sampler.node.StackTraceNode;
import me.lucko.spark.common.sampler.node.exporter.AbstractNodeExporter;
import me.lucko.spark.common.sampler.window.ProtoTimeEncoder;
import me.lucko.spark.proto.SparkSamplerProtos;

import java.util.Collection;

/**
* Node exporter for the {@link AsyncSampler}.
*/
public class AsyncNodeExporter extends AbstractNodeExporter {
public AsyncNodeExporter(ProtoTimeEncoder timeEncoder) {
super(timeEncoder);
}

@Override
protected SparkSamplerProtos.StackTraceNode export(StackTraceNode stackTraceNode, Iterable<Integer> childrenRefs) {
SparkSamplerProtos.StackTraceNode.Builder proto = SparkSamplerProtos.StackTraceNode.newBuilder()
.setClassName(stackTraceNode.getClassName())
.setMethodName(stackTraceNode.getMethodName());

double[] times = stackTraceNode.encodeTimesForProto(this.timeEncoder);
for (double time : times) {
proto.addTimes(time);
}

String methodDescription = stackTraceNode.getMethodDescription();
if (methodDescription != null) {
proto.setMethodDesc(methodDescription);
}

proto.addAllChildrenRefs(childrenRefs);

return proto.build();
}

@Override
protected Collection<StackTraceNode> exportChildren(Collection<StackTraceNode> children) {
return children;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public SamplerData toProto(SparkPlatform platform, ExportProps exportProps) {
proto.setChannelInfo(exportProps.channelInfo());
}
writeMetadataToProto(proto, platform, exportProps.creator(), exportProps.comment(), this.dataAggregator);
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get(), platform::createClassFinder);
writeDataToProto(proto, this.dataAggregator, AsyncNodeExporter::new, exportProps.classSourceLookup().get(), platform::createClassFinder);
return proto.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public abstract class JavaDataAggregator extends AbstractDataAggregator {
/** A describer for java.lang.StackTraceElement */
private static final StackTraceNode.Describer<StackTraceElement> STACK_TRACE_DESCRIBER = (element, parent) -> {
int parentLineNumber = parent == null ? StackTraceNode.NULL_LINE_NUMBER : parent.getLineNumber();
return new StackTraceNode.Description(element.getClassName(), element.getMethodName(), element.getLineNumber(), parentLineNumber);
return new StackTraceNode.JavaDescription(element.getClassName(), element.getMethodName(), element.getLineNumber(), parentLineNumber);
};

/** The worker pool for inserting stack nodes */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* This file is part of spark.
*
* Copyright (c) lucko (Luck) <[email protected]>
* Copyright (c) contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.sampler.java;

import me.lucko.spark.common.sampler.node.StackTraceNode;
import me.lucko.spark.common.sampler.node.exporter.AbstractNodeExporter;
import me.lucko.spark.common.sampler.window.ProtoTimeEncoder;
import me.lucko.spark.common.util.MethodDisambiguator;
import me.lucko.spark.proto.SparkSamplerProtos;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Node exporter for the {@link JavaSampler}.
*/
public class JavaNodeExporter extends AbstractNodeExporter {
private final MergeStrategy mergeStrategy;
private final MethodDisambiguator methodDisambiguator;

public JavaNodeExporter(ProtoTimeEncoder timeEncoder, MergeStrategy mergeStrategy, MethodDisambiguator methodDisambiguator) {
super(timeEncoder);
this.mergeStrategy = mergeStrategy;
this.methodDisambiguator = methodDisambiguator;
}

protected SparkSamplerProtos.StackTraceNode export(StackTraceNode stackTraceNode, Iterable<Integer> childrenRefs) {
SparkSamplerProtos.StackTraceNode.Builder proto = SparkSamplerProtos.StackTraceNode.newBuilder()
.setClassName(stackTraceNode.getClassName())
.setMethodName(stackTraceNode.getMethodName());

double[] times = stackTraceNode.encodeTimesForProto(this.timeEncoder);
for (double time : times) {
proto.addTimes(time);
}

int lineNumber = stackTraceNode.getLineNumber();
if (lineNumber >= 0) {
proto.setLineNumber(lineNumber);
}

if (this.mergeStrategy.separateParentCalls()) {
int parentLineNumber = stackTraceNode.getParentLineNumber();
if (parentLineNumber >= 0) {
proto.setParentLineNumber(parentLineNumber);
}
}

this.methodDisambiguator.disambiguate(stackTraceNode)
.map(MethodDisambiguator.MethodDescription::getDescription)
.ifPresent(proto::setMethodDesc);

proto.addAllChildrenRefs(childrenRefs);

return proto.build();
}

@Override
protected Collection<StackTraceNode> exportChildren(Collection<StackTraceNode> children) {
if (children.isEmpty()) {
return children;
}

List<StackTraceNode> list = new ArrayList<>(children.size());

outer:
for (StackTraceNode child : children) {
for (StackTraceNode other : list) {
if (this.mergeStrategy.shouldMerge(this.methodDisambiguator, other, child)) {
other.merge(child);
continue outer;
}
}
list.add(child);
}
return list;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import me.lucko.spark.common.sampler.window.ProfilingWindowUtils;
import me.lucko.spark.common.sampler.window.WindowStatisticsCollector;
import me.lucko.spark.common.tick.TickHook;
import me.lucko.spark.common.util.MethodDisambiguator;
import me.lucko.spark.common.util.SparkThreadFactory;
import me.lucko.spark.common.ws.ViewerSocket;
import me.lucko.spark.proto.SparkSamplerProtos.SamplerData;
Expand Down Expand Up @@ -192,8 +193,12 @@ public SamplerData toProto(SparkPlatform platform, ExportProps exportProps) {
if (exportProps.channelInfo() != null) {
proto.setChannelInfo(exportProps.channelInfo());
}

writeMetadataToProto(proto, platform, exportProps.creator(), exportProps.comment(), this.dataAggregator);
writeDataToProto(proto, this.dataAggregator, exportProps.mergeMode().get(), exportProps.classSourceLookup().get(), platform::createClassFinder);

MethodDisambiguator methodDisambiguator = new MethodDisambiguator(platform.createClassFinder());
writeDataToProto(proto, this.dataAggregator, timeEncoder -> new JavaNodeExporter(timeEncoder, exportProps.mergeStrategy(), methodDisambiguator), exportProps.classSourceLookup().get(), platform::createClassFinder);

return proto.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package me.lucko.spark.common.sampler.node;
package me.lucko.spark.common.sampler.java;

import me.lucko.spark.common.sampler.node.StackTraceNode;
import me.lucko.spark.common.util.MethodDisambiguator;

import java.util.Objects;

/**
* Function to determine if {@link StackTraceNode}s should be merged.
* Strategy used to determine if {@link StackTraceNode}s should be merged.
*/
public final class MergeMode {
public enum MergeStrategy {

public static MergeMode sameMethod(MethodDisambiguator methodDisambiguator) {
return new MergeMode(methodDisambiguator, false);
}

public static MergeMode separateParentCalls(MethodDisambiguator methodDisambiguator) {
return new MergeMode(methodDisambiguator, true);
}
SAME_METHOD(false),
SEPARATE_PARENT_CALLS(true);

private final MethodDisambiguator methodDisambiguator;
private final boolean separateParentCalls;

MergeMode(MethodDisambiguator methodDisambiguator, boolean separateParentCalls) {
this.methodDisambiguator = methodDisambiguator;
MergeStrategy(boolean separateParentCalls) {
this.separateParentCalls = separateParentCalls;
}

public MethodDisambiguator getMethodDisambiguator() {
return this.methodDisambiguator;
}

public boolean separateParentCalls() {
return this.separateParentCalls;
}

/**
* Test if two stack trace nodes should be considered the same and merged.
*
* @param disambiguator the method disambiguator
* @param n1 the first node
* @param n2 the second node
* @return if the nodes should be merged
*/
public boolean shouldMerge(StackTraceNode n1, StackTraceNode n2) {
public boolean shouldMerge(MethodDisambiguator disambiguator, StackTraceNode n1, StackTraceNode n2) {
// are the class names the same?
if (!n1.getClassName().equals(n2.getClassName())) {
return false;
Expand All @@ -77,8 +68,8 @@ public boolean shouldMerge(StackTraceNode n1, StackTraceNode n2) {
}

// are the method descriptions the same? (is it the same method?)
String desc1 = this.methodDisambiguator.disambiguate(n1).map(MethodDisambiguator.MethodDescription::getDesc).orElse(null);
String desc2 = this.methodDisambiguator.disambiguate(n2).map(MethodDisambiguator.MethodDescription::getDesc).orElse(null);
String desc1 = disambiguator.disambiguate(n1).map(MethodDisambiguator.MethodDescription::getDescription).orElse(null);
String desc2 = disambiguator.disambiguate(n2).map(MethodDisambiguator.MethodDescription::getDescription).orElse(null);

if (desc1 == null && desc2 == null) {
return true;
Expand Down
Loading

0 comments on commit 684cc5e

Please sign in to comment.