Skip to content

Commit

Permalink
feat: add some basic statistics to /orebfuscator command
Browse files Browse the repository at this point in the history
  • Loading branch information
Ingrim4 committed Aug 22, 2024
1 parent 17f48de commit bf8c292
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 33 deletions.
6 changes: 0 additions & 6 deletions orebfuscator-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@
</build>

<dependencies>
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.17</version>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class Orebfuscator extends JavaPlugin implements Listener {

private final Thread mainThread = Thread.currentThread();

private OrebfuscatorStatistics statistics;
private OrebfuscatorConfig config;
private OrebfuscatorPlayerMap playerMap;
private UpdateSystem updateSystem;
Expand Down Expand Up @@ -54,6 +55,8 @@ public void onEnable() {
throw new RuntimeException("ProtocolLib can't be found or is disabled! Orebfuscator can't be enabled.");
}

this.statistics = new OrebfuscatorStatistics();

// Load configurations
this.config = new OrebfuscatorConfig(this);

Expand Down Expand Up @@ -138,6 +141,10 @@ public boolean isGameThread() {
return Thread.currentThread() == this.mainThread;
}

public OrebfuscatorStatistics getStatistics() {
return statistics;
}

public OrebfuscatorConfig getOrebfuscatorConfig() {
return this.config;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St

if (args.length == 0) {
sender.sendMessage("You are using " + this.orebfuscator.toString());
this.orebfuscator.getObfuscationCache().printEstimatedSize(sender);
sender.sendMessage(this.orebfuscator.getStatistics().toString());
} else if (args[0].equalsIgnoreCase("dump")) {
TemporalAccessor now = OffsetDateTime.now(ZoneOffset.UTC);

Expand All @@ -78,6 +78,8 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
versions.addProperty("orebfuscator", orebfuscator.toString());
root.add("versions", versions);

root.add("statistics", orebfuscator.getStatistics().toJson());

JsonObject plugins = new JsonObject();
for (Plugin bukkitPlugin : Bukkit.getPluginManager().getPlugins()) {
PluginDescriptionFile description = bukkitPlugin.getDescription();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package net.imprex.orebfuscator;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntSupplier;

import com.google.gson.JsonObject;

public class OrebfuscatorStatistics {

private static String formatPrecent(double percent) {
return String.format("%.2f%%", percent * 100);
}

private static String formatBytes(long bytes) {
if (bytes > 1073741824L) {
return String.format("%.1f GiB", bytes / 1073741824d);
} else if (bytes > 1048576L) {
return String.format("%.1f MiB", bytes / 1048576d);
} else if (bytes > 1024L) {
return String.format("%.1f KiB", bytes / 1024d);
} else {
return String.format("%d B", bytes);
}
}

private final AtomicLong cacheHitCountMemory = new AtomicLong(0);
private final AtomicLong cacheHitCountDisk = new AtomicLong(0);
private final AtomicLong cacheMissCount = new AtomicLong(0);
private final AtomicLong cacheEstimatedSize = new AtomicLong(0);
private IntSupplier diskCacheQueueLength = () -> 0;
private IntSupplier obfuscationQueueLength = () -> 0;

public void onCacheHitMemory() {
this.cacheHitCountMemory.incrementAndGet();
}

public void onCacheHitDisk() {
this.cacheHitCountDisk.incrementAndGet();
}

public void onCacheMiss() {
this.cacheMissCount.incrementAndGet();
}

public void onCacheSizeChange(int delta) {
this.cacheEstimatedSize.addAndGet(delta);
}

public void setDiskCacheQueueLengthSupplier(IntSupplier supplier) {
this.diskCacheQueueLength = Objects.requireNonNull(supplier);
}

public void setObfuscationQueueLengthSupplier(IntSupplier supplier) {
this.obfuscationQueueLength = Objects.requireNonNull(supplier);
}

@Override
public String toString() {
long cacheHitCountMemory = this.cacheHitCountMemory.get();
long cacheHitCountDisk = this.cacheHitCountDisk.get();
long cacheMissCount = this.cacheMissCount.get();
long cacheEstimatedSize = this.cacheEstimatedSize.get();
long diskCacheQueueLength = this.diskCacheQueueLength.getAsInt();
long obfuscationQueueLength = this.obfuscationQueueLength.getAsInt();

double totalCacheRequest = (double) (cacheHitCountMemory + cacheHitCountDisk + cacheMissCount);
double memoryCacheHitRate = (double) cacheHitCountMemory / totalCacheRequest;
double diskCacheHitRate = (double) cacheHitCountDisk / totalCacheRequest;

StringBuilder builder = new StringBuilder("Here are some useful statistics:\n");

builder.append(" - memoryCacheHitRate: ").append(formatPrecent(memoryCacheHitRate)).append('\n');
builder.append(" - diskCacheHitRate: ").append(formatPrecent(diskCacheHitRate)).append('\n');
builder.append(" - memoryCacheEstimatedSize: ").append(formatBytes(cacheEstimatedSize)).append('\n');
builder.append(" - diskCacheQueueLength: ").append(diskCacheQueueLength).append('\n');
builder.append(" - obfuscationQueueLength: ").append(obfuscationQueueLength).append('\n');

return builder.toString();
}

public JsonObject toJson() {
JsonObject object = new JsonObject();

object.addProperty("cacheHitCountMemory", this.cacheHitCountMemory.get());
object.addProperty("cacheHitCountDisk", this.cacheHitCountDisk.get());
object.addProperty("cacheMissCount", this.cacheMissCount.get());
object.addProperty("cacheEstimatedSize", this.cacheEstimatedSize.get());
object.addProperty("diskCacheQueueLength", this.diskCacheQueueLength.getAsInt());
object.addProperty("obfuscationQueueLength", this.obfuscationQueueLength.getAsInt());

return object;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public AsyncChunkSerializer(Orebfuscator orebfuscator) {
this.thread = new Thread(Orebfuscator.THREAD_GROUP, this, "ofc-chunk-serializer");
this.thread.setDaemon(true);
this.thread.start();

orebfuscator.getStatistics().setDiskCacheQueueLengthSupplier(() -> this.tasks.size());
}

public CompletableFuture<CompressedObfuscationResult> read(ChunkPosition position) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static void write(ChunkPosition key, CompressedObfuscationResult value) t
if (value != null) {
dataOutputStream.writeBoolean(true);

byte[] compressedData = value.getCompressedData();
byte[] compressedData = value.compressedData();
dataOutputStream.writeInt(compressedData.length);
dataOutputStream.write(compressedData);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ public CompressedObfuscationResult(ChunkPosition position, byte[] data) {
this.compressedData = data;
}

public byte[] getCompressedData() {
public byte[] compressedData() {
return compressedData;
}

public int estimatedSize() {
return 128 + this.compressedData.length;
}

public boolean isValid(ObfuscationRequest request) {
try {
return request != null && Arrays.equals(this.compressedData, 0, ObfuscationRequest.HASH_LENGTH,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import org.bukkit.command.CommandSender;
import org.openjdk.jol.info.GraphLayout;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;

import net.imprex.orebfuscator.Orebfuscator;
import net.imprex.orebfuscator.OrebfuscatorCompatibility;
import net.imprex.orebfuscator.OrebfuscatorStatistics;
import net.imprex.orebfuscator.config.CacheConfig;
import net.imprex.orebfuscator.obfuscation.ObfuscationRequest;
import net.imprex.orebfuscator.obfuscation.ObfuscationResult;
Expand All @@ -21,13 +19,15 @@ public class ObfuscationCache {

private final Orebfuscator orebfuscator;
private final CacheConfig cacheConfig;
private final OrebfuscatorStatistics statistics;

private final Cache<ChunkPosition, CompressedObfuscationResult> cache;
private final AsyncChunkSerializer serializer;

public ObfuscationCache(Orebfuscator orebfuscator) {
this.orebfuscator = orebfuscator;
this.cacheConfig = orebfuscator.getOrebfuscatorConfig().cache();
this.statistics = orebfuscator.getStatistics();

this.cache = CacheBuilder.newBuilder()
.maximumSize(this.cacheConfig.maximumSize())
Expand All @@ -46,27 +46,9 @@ public ObfuscationCache(Orebfuscator orebfuscator) {
}
}

public void printEstimatedSize(CommandSender sender) {
OrebfuscatorCompatibility.runAsyncNow(() -> {
long cacheSize = this.cache.size();

double bytes = this.cache.asMap().values().stream()
.mapToLong(entry -> GraphLayout.parseInstance(entry).totalSize())
.sum() / 1024d;
double decompressedBytes = this.cache.asMap().values().stream()
.map(entry -> entry.toResult())
.mapToLong(entry -> GraphLayout.parseInstance(entry).totalSize())
.sum() / 1024d;

sender.sendMessage(String.format("count: %d, heap: %.1f/%.1f MiB, avg: %.1f/%.1f KiB, pct: %.1f%%",
cacheSize,
(bytes / 1024d), (decompressedBytes / 1024d),
(bytes / cacheSize), (decompressedBytes / cacheSize),
(bytes / decompressedBytes * 100d)));
});
}

private void onRemoval(RemovalNotification<ChunkPosition, CompressedObfuscationResult> notification) {
this.statistics.onCacheSizeChange(-notification.getValue().estimatedSize());

// don't serialize invalidated chunks since this would require locking the main
// thread and wouldn't bring a huge improvement
if (this.cacheConfig.enableDiskCache() && notification.wasEvicted() && !this.orebfuscator.isGameThread()) {
Expand All @@ -79,6 +61,7 @@ private void requestObfuscation(ObfuscationRequest request) {
var compressedChunk = CompressedObfuscationResult.create(chunk);
if (compressedChunk != null) {
this.cache.put(request.getPosition(), compressedChunk);
this.statistics.onCacheSizeChange(compressedChunk.estimatedSize());
}
});
}
Expand All @@ -88,18 +71,30 @@ public CompletableFuture<ObfuscationResult> get(ObfuscationRequest request) {

CompressedObfuscationResult cacheChunk = this.cache.getIfPresent(key);
if (cacheChunk != null && cacheChunk.isValid(request)) {
cacheChunk.toResult().ifPresent(request::complete);
this.statistics.onCacheHitMemory();

// complete request
cacheChunk.toResult().ifPresentOrElse(request::complete,
// request obfuscation if decoding failed
() -> this.requestObfuscation(request));
}
// only access disk cache if we couldn't find the chunk in memory cache
else if (cacheChunk == null && this.cacheConfig.enableDiskCache()) {
this.serializer.read(key).whenComplete((diskChunk, throwable) -> {
if (diskChunk != null && diskChunk.isValid(request)) {
this.statistics.onCacheHitDisk();

// add valid disk cache entry to in-memory cache
this.cache.put(key, diskChunk);
this.statistics.onCacheSizeChange(diskChunk.estimatedSize());

// complete request
diskChunk.toResult().ifPresentOrElse(request::complete,
// request obfuscation if decoding failed
() -> this.requestObfuscation(request));
} else {
this.statistics.onCacheMiss();

// request obfuscation if disk cache missing
this.requestObfuscation(request);
}
Expand All @@ -113,6 +108,7 @@ else if (cacheChunk == null && this.cacheConfig.enableDiskCache()) {
}
// request obfuscation if cache missed
else {
this.statistics.onCacheMiss();
this.requestObfuscation(request);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public ObfuscationTaskDispatcher(Orebfuscator orebfuscator, ObfuscationProcessor
for (int i = 0; i < this.worker.length; i++) {
this.worker[i] = new ObfuscationTaskWorker(this, this.processor);
}

orebfuscator.getStatistics().setObfuscationQueueLengthSupplier(() -> this.tasks.size());
}

public void submitRequest(ObfuscationRequest request) {
Expand Down

0 comments on commit bf8c292

Please sign in to comment.