aboutsummaryrefslogtreecommitdiff
path: root/spark-common
diff options
context:
space:
mode:
Diffstat (limited to 'spark-common')
-rw-r--r--spark-common/build.gradle13
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java2
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java23
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java23
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java179
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java17
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/ThreadDumper.java25
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/AbstractDataAggregator.java63
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java7
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncDataAggregator.java53
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncProfilerAccess.java117
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncSampler.java279
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/AsyncStackTraceElement.java52
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/ProfileSegment.java61
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/ClassRef.java25
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Dictionary.java107
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Element.java23
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrClass.java49
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrField.java31
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/JfrReader.java389
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/MethodRef.java29
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/Sample.java36
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/async/jfr/StackTrace.java28
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaDataAggregator.java116
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/JavaSampler.java189
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/SimpleDataAggregator.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/SimpleDataAggregator.java)20
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/java/TickedDataAggregator.java (renamed from spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/TickedDataAggregator.java)15
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/node/AbstractNode.java30
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/sampler/node/StackTraceNode.java58
-rwxr-xr-xspark-common/src/main/resources/libasyncProfiler.sobin0 -> 309494 bytes
30 files changed, 1801 insertions, 258 deletions
diff --git a/spark-common/build.gradle b/spark-common/build.gradle
index 3f113dd..8b64449 100644
--- a/spark-common/build.gradle
+++ b/spark-common/build.gradle
@@ -3,19 +3,20 @@ plugins {
}
dependencies {
+ compile 'com.github.jvm-profiling-tools:async-profiler:v2.0-rc'
compile 'org.ow2.asm:asm:7.1'
compile 'com.google.protobuf:protobuf-java:3.14.0'
compile 'com.squareup.okhttp3:okhttp:3.14.1'
compile 'com.squareup.okio:okio:1.17.3'
compile 'org.tukaani:xz:1.8'
- compile('net.kyori:adventure-api:4.1.1') {
+ compile('net.kyori:adventure-api:4.7.0') {
exclude(module: 'checker-qual')
}
- compile('net.kyori:adventure-text-serializer-gson:4.1.1') {
+ compile('net.kyori:adventure-text-serializer-gson:4.7.0') {
exclude(module: 'adventure-api')
exclude(module: 'gson')
}
- compile('net.kyori:adventure-text-serializer-legacy:4.1.1') {
+ compile('net.kyori:adventure-text-serializer-legacy:4.7.0') {
exclude(module: 'adventure-api')
}
compile('net.kyori:adventure-text-feature-pagination:4.0.0-SNAPSHOT') {
@@ -25,6 +26,12 @@ dependencies {
compileOnly 'com.google.guava:guava:19.0'
}
+processResources {
+ from(sourceSets.main.resources.srcDirs) {
+ include 'libasyncProfiler.so'
+ }
+}
+
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.14.0'
diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
index cf3b0ee..a6bf332 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java
@@ -211,7 +211,7 @@ public class SparkPlatform {
if (command.aliases().contains(alias)) {
try {
command.executor().execute(this, sender, resp, new Arguments(rawArgs));
- } catch (IllegalArgumentException e) {
+ } catch (Arguments.ParseException e) {
resp.replyPrefixed(text(e.getMessage(), RED));
}
return;
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
index 2b202af..3cd0365 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java
@@ -51,7 +51,7 @@ public class Arguments {
if (flag == null || matches) {
if (!matches) {
- throw new IllegalArgumentException("Expected flag at position " + i + " but got '" + arg + "' instead!");
+ throw new ParseException("Expected flag at position " + i + " but got '" + arg + "' instead!");
}
// store existing value, if present
@@ -83,7 +83,7 @@ public class Arguments {
try {
return Math.abs(Integer.parseInt(it.next()));
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid input for '" + key + "' argument. Please specify a number!");
+ throw new ParseException("Invalid input for '" + key + "' argument. Please specify a number!");
}
}
return -1; // undefined
@@ -95,7 +95,7 @@ public class Arguments {
try {
return Math.abs(Double.parseDouble(it.next()));
} catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid input for '" + key + "' argument. Please specify a number!");
+ throw new ParseException("Invalid input for '" + key + "' argument. Please specify a number!");
}
}
return -1; // undefined
@@ -108,4 +108,21 @@ public class Arguments {
public boolean boolFlag(String key) {
return this.parsedArgs.containsKey(key);
}
+
+ public static final class ParseException extends IllegalArgumentException {
+ public ParseException() {
+ }
+
+ public ParseException(String s) {
+ super(s);
+ }
+
+ public ParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ParseException(Throwable cause) {
+ super(cause);
+ }
+ }
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
index eb77b24..cce3169 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java
@@ -33,6 +33,7 @@ import me.lucko.spark.common.sampler.SamplerBuilder;
import me.lucko.spark.common.sampler.ThreadDumper;
import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.ThreadNodeOrder;
+import me.lucko.spark.common.sampler.async.AsyncSampler;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.tick.TickHook;
import me.lucko.spark.common.util.MethodDisambiguator;
@@ -55,13 +56,13 @@ import static net.kyori.adventure.text.format.NamedTextColor.*;
public class SamplerModule implements CommandModule {
private static final MediaType SPARK_SAMPLER_MEDIA_TYPE = MediaType.parse("application/x-spark-sampler");
- /** The WarmRoast instance currently running, if any */
+ /** The sampler instance currently running, if any */
private Sampler activeSampler = null;
@Override
public void close() {
if (this.activeSampler != null) {
- this.activeSampler.cancel();
+ this.activeSampler.stop();
this.activeSampler = null;
}
}
@@ -83,6 +84,7 @@ public class SamplerModule implements CommandModule {
.argumentUsage("only-ticks-over", "tick length millis")
.argumentUsage("ignore-sleeping", null)
.argumentUsage("ignore-native", null)
+ .argumentUsage("force-java-sampler", null)
.argumentUsage("order-by-time", null)
.argumentUsage("separate-parent-calls", null)
.executor((platform, sender, resp, arguments) -> {
@@ -118,7 +120,7 @@ public class SamplerModule implements CommandModule {
if (this.activeSampler == null) {
resp.replyPrefixed(text("There isn't an active sampling task running."));
} else {
- this.activeSampler.cancel();
+ this.activeSampler.stop();
resp.broadcastPrefixed(text("The active sampling operation has been stopped! Uploading results..."));
ThreadNodeOrder threadOrder = arguments.boolFlag("order-by-time") ? ThreadNodeOrder.BY_TIME : ThreadNodeOrder.BY_NAME;
String comment = Iterables.getFirst(arguments.stringFlag("comment"), null);
@@ -149,6 +151,7 @@ public class SamplerModule implements CommandModule {
boolean ignoreSleeping = arguments.boolFlag("ignore-sleeping");
boolean ignoreNative = arguments.boolFlag("ignore-native");
+ boolean forceJavaSampler = arguments.boolFlag("force-java-sampler");
Set<String> threads = arguments.stringFlag("thread");
ThreadDumper threadDumper;
@@ -201,19 +204,25 @@ public class SamplerModule implements CommandModule {
builder.samplingInterval(intervalMillis);
builder.ignoreSleeping(ignoreSleeping);
builder.ignoreNative(ignoreNative);
+ builder.forceJavaSampler(forceJavaSampler);
if (ticksOver != -1) {
builder.ticksOver(ticksOver, tickHook);
}
Sampler sampler = this.activeSampler = builder.start();
- resp.broadcastPrefixed(text("Profiler now active!", GOLD));
+ resp.broadcastPrefixed(text()
+ .append(text("Profiler now active!", GOLD))
+ .append(space())
+ .append(text("(" + (sampler instanceof AsyncSampler ? "async" : "built-in java") + ")", DARK_GRAY))
+ .build()
+ );
if (timeoutSeconds == -1) {
resp.broadcastPrefixed(text("Use '/" + platform.getPlugin().getCommandName() + " profiler --stop' to stop profiling and upload the results."));
} else {
resp.broadcastPrefixed(text("The results will be automatically returned after the profiler has been running for " + timeoutSeconds + " seconds."));
}
- CompletableFuture<Sampler> future = this.activeSampler.getFuture();
+ CompletableFuture<? extends Sampler> future = this.activeSampler.getFuture();
// send message if profiling fails
future.whenCompleteAsync((s, throwable) -> {
@@ -253,8 +262,8 @@ public class SamplerModule implements CommandModule {
List<String> opts = new ArrayList<>(Arrays.asList("--info", "--stop", "--cancel",
"--timeout", "--regex", "--combine-all", "--not-combined", "--interval",
- "--only-ticks-over", "--ignore-sleeping", "--ignore-native", "--order-by-time",
- "--separate-parent-calls", "--comment"));
+ "--only-ticks-over", "--ignore-sleeping", "--ignore-native", "--force-java-sampler",
+ "--order-by-time", "--separate-parent-calls", "--comment"));
opts.removeAll(arguments);
opts.add("--thread"); // allowed multiple times
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java
index e772cb3..5088ed7 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/Sampler.java
@@ -1,7 +1,6 @@
/*
* This file is part of spark.
*
- * Copyright (C) Albert Pham <http://www.sk89q.com>
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
@@ -21,174 +20,66 @@
package me.lucko.spark.common.sampler;
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
import me.lucko.spark.common.command.sender.CommandSender;
import me.lucko.spark.common.platform.PlatformInfo;
-import me.lucko.spark.common.sampler.aggregator.DataAggregator;
-import me.lucko.spark.common.sampler.aggregator.SimpleDataAggregator;
-import me.lucko.spark.common.sampler.aggregator.TickedDataAggregator;
import me.lucko.spark.common.sampler.node.MergeMode;
import me.lucko.spark.common.sampler.node.ThreadNode;
-import me.lucko.spark.common.sampler.tick.TickHook;
import me.lucko.spark.proto.SparkProtos.SamplerData;
-import me.lucko.spark.proto.SparkProtos.SamplerMetadata;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPOutputStream;
/**
- * Main sampler class.
+ * Abstract superinterface for all sampler implementations.
*/
-public class Sampler implements Runnable {
- private static final AtomicInteger THREAD_ID = new AtomicInteger(0);
-
- /** The worker pool for inserting stack nodes */
- private final ScheduledExecutorService workerPool = Executors.newScheduledThreadPool(
- 6, new ThreadFactoryBuilder().setNameFormat("spark-worker-" + THREAD_ID.getAndIncrement() + "-%d").build()
- );
-
- /** The main sampling task */
- private ScheduledFuture<?> task;
-
- /** The thread management interface for the current JVM */
- private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
- /** The instance used to generate thread information for use in sampling */
- private final ThreadDumper threadDumper;
- /** Responsible for aggregating and then outputting collected sampling data */
- private final DataAggregator dataAggregator;
-
- /** A future to encapsulation the completion of this sampler instance */
- private final CompletableFuture<Sampler> future = new CompletableFuture<>();
-
- /** The interval to wait between sampling, in microseconds */
- private final int interval;
- /** The time when sampling first began */
- private long startTime = -1;
- /** The unix timestamp (in millis) when this sampler should automatically complete.*/
- private final long endTime; // -1 for nothing
-
- public Sampler(int interval, ThreadDumper threadDumper, ThreadGrouper threadGrouper, long endTime, boolean ignoreSleeping, boolean ignoreNative) {
- this.threadDumper = threadDumper;
- this.dataAggregator = new SimpleDataAggregator(this.workerPool, threadGrouper, interval, ignoreSleeping, ignoreNative);
- this.interval = interval;
- this.endTime = endTime;
- }
-
- public Sampler(int interval, ThreadDumper threadDumper, ThreadGrouper threadGrouper, long endTime, boolean ignoreSleeping, boolean ignoreNative, TickHook tickHook, int tickLengthThreshold) {
- this.threadDumper = threadDumper;
- this.dataAggregator = new TickedDataAggregator(this.workerPool, threadGrouper, interval, ignoreSleeping, ignoreNative, tickHook, tickLengthThreshold);
- this.interval = interval;
- this.endTime = endTime;
- }
+public interface Sampler {
/**
* Starts the sampler.
*/
- public void start() {
- this.startTime = System.currentTimeMillis();
- this.task = this.workerPool.scheduleAtFixedRate(this, 0, this.interval, TimeUnit.MICROSECONDS);
- }
-
- public long getStartTime() {
- if (this.startTime == -1) {
- throw new IllegalStateException("Not yet started");
- }
- return this.startTime;
- }
-
- public long getEndTime() {
- return this.endTime;
- }
-
- public CompletableFuture<Sampler> getFuture() {
- return this.future;
- }
+ void start();
- public void cancel() {
- this.task.cancel(false);
- }
-
- @Override
- public void run() {
- // this is effectively synchronized, the worker pool will not allow this task
- // to concurrently execute.
- try {
- if (this.endTime != -1 && this.endTime <= System.currentTimeMillis()) {
- this.future.complete(this);
- cancel();
- return;
- }
-
- ThreadInfo[] threadDumps = this.threadDumper.dumpThreads(this.threadBean);
- this.workerPool.execute(new InsertDataTask(this.dataAggregator, threadDumps));
- } catch (Throwable t) {
- this.future.completeExceptionally(t);
- cancel();
- }
- }
-
- private static final class InsertDataTask implements Runnable {
- private final DataAggregator dataAggregator;
- private final ThreadInfo[] threadDumps;
-
- InsertDataTask(DataAggregator dataAggregator, ThreadInfo[] threadDumps) {
- this.dataAggregator = dataAggregator;
- this.threadDumps = threadDumps;
- }
-
- @Override
- public void run() {
- for (ThreadInfo threadInfo : this.threadDumps) {
- if (threadInfo.getThreadName() == null || threadInfo.getStackTrace() == null) {
- continue;
- }
- this.dataAggregator.insertData(threadInfo);
- }
- }
- }
-
- private SamplerData toProto(PlatformInfo platformInfo, CommandSender creator, Comparator<? super Map.Entry<String, ThreadNode>> outputOrder, String comment, MergeMode mergeMode) {
- final SamplerMetadata.Builder metadata = SamplerMetadata.newBuilder()
- .setPlatform(platformInfo.toData().toProto())
- .setUser(creator.toData().toProto())
- .setStartTime(this.startTime)
- .setInterval(this.interval)
- .setThreadDumper(this.threadDumper.getMetadata())
- .setDataAggregator(this.dataAggregator.getMetadata());
-
- if (comment != null) {
- metadata.setComment(comment);
- }
-
- SamplerData.Builder proto = SamplerData.newBuilder();
- proto.setMetadata(metadata.build());
+ /**
+ * Stops the sampler.
+ */
+ void stop();
- List<Map.Entry<String, ThreadNode>> data = new ArrayList<>(this.dataAggregator.getData().entrySet());
- data.sort(outputOrder);
+ /**
+ * Gets the time when the sampler started (unix timestamp in millis)
+ *
+ * @return the start time
+ */
+ long getStartTime();
- for (Map.Entry<String, ThreadNode> entry : data) {
- proto.addThreads(entry.getValue().toProto(mergeMode));
- }
+ /**
+ * Gets the time when the sampler should automatically stop (unix timestamp in millis)
+ *
+ * @return the end time, or -1 if undefined
+ */
+ long getEndTime();
- return proto.build();
- }
+ /**
+ * Gets a future to encapsulate the completion of the sampler
+ *
+ * @return a future
+ */
+ CompletableFuture<? extends Sampler> getFuture();
+
+ // Methods used to export the sampler data to the web viewer.
+ SamplerData toProto(
+ PlatformInfo platformInfo,
+ CommandSender creator,
+ Comparator<? super Map.Entry<String, ThreadNode>> outputOrder,
+ String comment,
+ MergeMode mergeMode
+ );
- public byte[] formCompressedDataPayload(PlatformInfo platformInfo, CommandSender creator, Comparator<? super Map.Entry<String, ThreadNode>> outputOrder, String comment, MergeMode mergeMode) {
+ default byte[] formCompressedDataPayload(PlatformInfo platformInfo, CommandSender creator, Comparator<? super Map.Entry<String, ThreadNode>> outputOrder, String comment, MergeMode mergeMode) {
SamplerData proto = toProto(platformInfo, creator, outputOrder, comment, mergeMode);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java
index 8993445..7abe1a7 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/SamplerBuilder.java
@@ -20,6 +20,9 @@
package me.lucko.spark.common.sampler;
+import me.lucko.spark.common.sampler.async.AsyncProfilerAccess;
+import me.lucko.spark.common.sampler.async.AsyncSampler;
+import me.lucko.spark.common.sampler.java.JavaSampler;
import me.lucko.spark.common.sampler.tick.TickHook;
import java.util.concurrent.TimeUnit;
@@ -32,6 +35,7 @@ public class SamplerBuilder {
private double samplingInterval = 4; // milliseconds
private boolean ignoreSleeping = false;
private boolean ignoreNative = false;
+ private boolean useAsyncProfiler = true;
private long timeout = -1;
private ThreadDumper threadDumper = ThreadDumper.ALL;
private ThreadGrouper threadGrouper = ThreadGrouper.BY_NAME;
@@ -81,14 +85,23 @@ public class SamplerBuilder {
return this;
}
+ public SamplerBuilder forceJavaSampler(boolean forceJavaSampler) {
+ this.useAsyncProfiler = !forceJavaSampler;
+ return this;
+ }
+
public Sampler start() {
Sampler sampler;
int intervalMicros = (int) (this.samplingInterval * 1000d);
if (this.ticksOver == -1 || this.tickHook == null) {
- sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.ignoreSleeping, this.ignoreNative);
+ if (this.useAsyncProfiler && this.timeout == -1 && !(this.threadDumper instanceof ThreadDumper.Regex) && AsyncProfilerAccess.INSTANCE.isSupported()) {
+ sampler = new AsyncSampler(intervalMicros, this.threadDumper, this.threadGrouper);
+ } else {
+ sampler = new JavaSampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.ignoreSleeping, this.ignoreNative);
+ }
} else {
- sampler = new Sampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.ignoreSleeping, this.ignoreNative, this.tickHook, this.ticksOver);
+ sampler = new JavaSampler(intervalMicros, this.threadDumper, this.threadGrouper, this.timeout, this.ignoreSleeping, this.ignoreNative, this.tickHook, this.ticksOver);
}
sampler.start();
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/ThreadDumper.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/ThreadDumper.java
index 224918e..4863482 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/ThreadDumper.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/ThreadDumper.java
@@ -76,17 +76,38 @@ public interface ThreadDumper {
*/
final class Specific implements ThreadDumper {
private final long[] ids;
+ private Set<Thread> threads;
+ private Set<String> threadNamesLowerCase;
public Specific(long[] ids) {
this.ids = ids;
}
public Specific(Set<String> names) {
- Set<String> namesLower = names.stream().map(String::toLowerCase).collect(Collectors.toSet());
+ this.threadNamesLowerCase = names.stream().map(String::toLowerCase).collect(Collectors.toSet());
this.ids = new ThreadFinder().getThreads()
- .filter(t -> namesLower.contains(t.getName().toLowerCase()))
+ .filter(t -> this.threadNamesLowerCase.contains(t.getName().toLowerCase()))
.mapToLong(Thread::getId)
.toArray();
+ Arrays.sort(this.ids);
+ }
+
+ public Set<Thread> getThreads() {
+ if (this.threads == null) {
+ this.threads = new ThreadFinder().getThreads()
+ .filter(t -> Arrays.binarySearch(this.ids, t.getId()) >= 0)
+ .collect(Collectors.toSet());
+ }
+ return this.threads;
+ }
+
+ public Set<String> getThreadNames() {
+ if (this.threadNamesLowerCase == null) {
+ this.threadNamesLowerCase = getThreads().stream()
+ .map(t -> t.getName().toLowerCase())
+ .collect(Collectors.toSet());
+ }
+ return this.threadNamesLowerCase;
}
@Override
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/AbstractDataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/AbstractDataAggregator.java
index c9a4f37..7640d60 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/AbstractDataAggregator.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/AbstractDataAggregator.java
@@ -23,37 +23,22 @@ package me.lucko.spark.common.sampler.aggregator;
import me.lucko.spark.common.sampler.ThreadGrouper;
import me.lucko.spark.common.sampler.node.ThreadNode;
-import java.lang.management.ThreadInfo;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
+/**
+ * Abstract implementation of {@link DataAggregator}.
+ */
public abstract class AbstractDataAggregator implements DataAggregator {
/** A map of root stack nodes for each thread with sampling data */
protected final Map<String, ThreadNode> threadData = new ConcurrentHashMap<>();
- /** The worker pool for inserting stack nodes */
- protected final ExecutorService workerPool;
-
/** The instance used to group threads together */
protected final ThreadGrouper threadGrouper;
- /** The interval to wait between sampling, in microseconds */
- protected final int interval;
-
- /** If sleeping threads should be ignored */
- private final boolean ignoreSleeping;
-
- /** If threads executing native code should be ignored */
- private final boolean ignoreNative;
-
- public AbstractDataAggregator(ExecutorService workerPool, ThreadGrouper threadGrouper, int interval, boolean ignoreSleeping, boolean ignoreNative) {
- this.workerPool = workerPool;
+ protected AbstractDataAggregator(ThreadGrouper threadGrouper) {
this.threadGrouper = threadGrouper;
- this.interval = interval;
- this.ignoreSleeping = ignoreSleeping;
- this.ignoreNative = ignoreNative;
}
protected ThreadNode getNode(String group) {
@@ -64,42 +49,8 @@ public abstract class AbstractDataAggregator implements DataAggregator {
return this.threadData.computeIfAbsent(group, ThreadNode::new);
}
- protected void writeData(ThreadInfo threadInfo) {
- if (this.ignoreSleeping && isSleeping(threadInfo)) {
- return;
- }
- if (this.ignoreNative && threadInfo.isInNative()) {
- return;
- }
-
- try {
- ThreadNode node = getNode(this.threadGrouper.getGroup(threadInfo.getThreadId(), threadInfo.getThreadName()));
- node.log(threadInfo.getStackTrace(), this.interval);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ @Override
+ public Map<String, ThreadNode> getData() {
+ return this.threadData;
}
-
- private static boolean isSleeping(ThreadInfo thread) {
- if (thread.getThreadState() == Thread.State.WAITING || thread.getThreadState() == Thread.State.TIMED_WAITING) {
- return true;
- }
-
- StackTraceElement[] stackTrace = thread.getStackTrace();
- if (stackTrace.length == 0) {
- return false;
- }
-
- StackTraceElement call = stackTrace[0];
- String clazz = call.getClassName();
- String method = call.getMethodName();
-
- // java.lang.Thread.yield()
- // jdk.internal.misc.Unsafe.park()
- // sun.misc.Unsafe.park()
- return (clazz.equals("java.lang.Thread") && method.equals("yield")) ||
- (clazz.equals("jdk.internal.misc.Unsafe") && method.equals("park")) ||
- (clazz.equals("sun.misc.Unsafe") && method.equals("park"));
- }
-
}
diff --git a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
index 8318fbd..a91a998 100644
--- a/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
+++ b/spark-common/src/main/java/me/lucko/spark/common/sampler/aggregator/DataAggregator.java
@@ -39,13 +39,6 @@ public interface DataAggregator {
Map<String, ThreadNode> getData();
/**
- * Inserts sampling data into this aggregator
- *
- * @param threadInfo the thread info
- */
- void insertData(ThreadInfo threadInfo);