package net.minecraft.util.profiling; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongMaps; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import net.minecraft.ReportType; import net.minecraft.SharedConstants; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; import org.slf4j.Logger; public class FilledProfileResults implements ProfileResults { private static final Logger LOGGER = LogUtils.getLogger(); private static final ProfilerPathEntry EMPTY = new ProfilerPathEntry() { @Override public long getDuration() { return 0L; } @Override public long getMaxDuration() { return 0L; } @Override public long getCount() { return 0L; } @Override public Object2LongMap getCounters() { return Object2LongMaps.emptyMap(); } }; private static final Splitter SPLITTER = Splitter.on('\u001e'); private static final Comparator> COUNTER_ENTRY_COMPARATOR = Entry.comparingByValue( Comparator.comparingLong(p_18489_ -> p_18489_.totalValue) ) .reversed(); private final Map entries; private final long startTimeNano; private final int startTimeTicks; private final long endTimeNano; private final int endTimeTicks; private final int tickDuration; public FilledProfileResults(Map p_18464_, long p_18465_, int p_18466_, long p_18467_, int p_18468_) { this.entries = p_18464_; this.startTimeNano = p_18465_; this.startTimeTicks = p_18466_; this.endTimeNano = p_18467_; this.endTimeTicks = p_18468_; this.tickDuration = p_18468_ - p_18466_; } private ProfilerPathEntry getEntry(String p_18526_) { ProfilerPathEntry profilerpathentry = this.entries.get(p_18526_); return profilerpathentry != null ? profilerpathentry : EMPTY; } @Override public List getTimes(String p_18493_) { String s = p_18493_; ProfilerPathEntry profilerpathentry = this.getEntry("root"); long i = profilerpathentry.getDuration(); ProfilerPathEntry profilerpathentry1 = this.getEntry(p_18493_); long j = profilerpathentry1.getDuration(); long k = profilerpathentry1.getCount(); List list = Lists.newArrayList(); if (!p_18493_.isEmpty()) { p_18493_ = p_18493_ + "\u001e"; } long l = 0L; for (String s1 : this.entries.keySet()) { if (isDirectChild(p_18493_, s1)) { l += this.getEntry(s1).getDuration(); } } float f = (float)l; if (l < j) { l = j; } if (i < l) { i = l; } for (String s2 : this.entries.keySet()) { if (isDirectChild(p_18493_, s2)) { ProfilerPathEntry profilerpathentry2 = this.getEntry(s2); long i1 = profilerpathentry2.getDuration(); double d0 = i1 * 100.0 / l; double d1 = i1 * 100.0 / i; String s3 = s2.substring(p_18493_.length()); list.add(new ResultField(s3, d0, d1, profilerpathentry2.getCount())); } } if ((float)l > f) { list.add(new ResultField("unspecified", ((float)l - f) * 100.0 / l, ((float)l - f) * 100.0 / i, k)); } Collections.sort(list); list.add(0, new ResultField(s, 100.0, l * 100.0 / i, k)); return list; } private static boolean isDirectChild(String p_18495_, String p_18496_) { return p_18496_.length() > p_18495_.length() && p_18496_.startsWith(p_18495_) && p_18496_.indexOf(30, p_18495_.length() + 1) < 0; } private Map getCounterValues() { Map map = Maps.newTreeMap(); this.entries .forEach( (p_18512_, p_18513_) -> { Object2LongMap object2longmap = p_18513_.getCounters(); if (!object2longmap.isEmpty()) { List list = SPLITTER.splitToList(p_18512_); object2longmap.forEach( (p_145944_, p_145945_) -> map.computeIfAbsent(p_145944_, p_145947_ -> new FilledProfileResults.CounterCollector()) .addValue(list.iterator(), p_145945_) ); } } ); return map; } @Override public long getStartTimeNano() { return this.startTimeNano; } @Override public int getStartTimeTicks() { return this.startTimeTicks; } @Override public long getEndTimeNano() { return this.endTimeNano; } @Override public int getEndTimeTicks() { return this.endTimeTicks; } @Override public boolean saveResults(Path p_145940_) { Writer writer = null; boolean flag; try { Files.createDirectories(p_145940_.getParent()); writer = Files.newBufferedWriter(p_145940_, StandardCharsets.UTF_8); writer.write(this.getProfilerResults(this.getNanoDuration(), this.getTickDuration())); return true; } catch (Throwable throwable) { LOGGER.error("Could not save profiler results to {}", p_145940_, throwable); flag = false; } finally { IOUtils.closeQuietly(writer); } return flag; } protected String getProfilerResults(long p_18486_, int p_18487_) { StringBuilder stringbuilder = new StringBuilder(); ReportType.PROFILE.appendHeader(stringbuilder, List.of()); stringbuilder.append("Version: ").append(SharedConstants.getCurrentVersion().getId()).append('\n'); stringbuilder.append("Time span: ").append(p_18486_ / 1000000L).append(" ms\n"); stringbuilder.append("Tick span: ").append(p_18487_).append(" ticks\n"); stringbuilder.append("// This is approximately ") .append(String.format(Locale.ROOT, "%.2f", p_18487_ / ((float)p_18486_ / 1.0E9F))) .append(" ticks per second. It should be ") .append(20) .append(" ticks per second\n\n"); stringbuilder.append("--- BEGIN PROFILE DUMP ---\n\n"); this.appendProfilerResults(0, "root", stringbuilder); stringbuilder.append("--- END PROFILE DUMP ---\n\n"); Map map = this.getCounterValues(); if (!map.isEmpty()) { stringbuilder.append("--- BEGIN COUNTER DUMP ---\n\n"); this.appendCounters(map, stringbuilder, p_18487_); stringbuilder.append("--- END COUNTER DUMP ---\n\n"); } return stringbuilder.toString(); } @Override public String getProfilerResults() { StringBuilder stringbuilder = new StringBuilder(); this.appendProfilerResults(0, "root", stringbuilder); return stringbuilder.toString(); } private static StringBuilder indentLine(StringBuilder p_18498_, int p_18499_) { p_18498_.append(String.format(Locale.ROOT, "[%02d] ", p_18499_)); for (int i = 0; i < p_18499_; i++) { p_18498_.append("| "); } return p_18498_; } private void appendProfilerResults(int p_18482_, String p_18483_, StringBuilder p_18484_) { List list = this.getTimes(p_18483_); Object2LongMap object2longmap = ObjectUtils.firstNonNull(this.entries.get(p_18483_), EMPTY).getCounters(); object2longmap.forEach( (p_18508_, p_18509_) -> indentLine(p_18484_, p_18482_) .append('#') .append(p_18508_) .append(' ') .append(p_18509_) .append('/') .append(p_18509_ / this.tickDuration) .append('\n') ); if (list.size() >= 3) { for (int i = 1; i < list.size(); i++) { ResultField resultfield = list.get(i); indentLine(p_18484_, p_18482_) .append(resultfield.name) .append('(') .append(resultfield.count) .append('/') .append(String.format(Locale.ROOT, "%.0f", (float)resultfield.count / this.tickDuration)) .append(')') .append(" - ") .append(String.format(Locale.ROOT, "%.2f", resultfield.percentage)) .append("%/") .append(String.format(Locale.ROOT, "%.2f", resultfield.globalPercentage)) .append("%\n"); if (!"unspecified".equals(resultfield.name)) { try { this.appendProfilerResults(p_18482_ + 1, p_18483_ + "\u001e" + resultfield.name, p_18484_); } catch (Exception exception) { p_18484_.append("[[ EXCEPTION ").append(exception).append(" ]]"); } } } } } private void appendCounterResults(int p_18476_, String p_18477_, FilledProfileResults.CounterCollector p_18478_, int p_18479_, StringBuilder p_18480_) { indentLine(p_18480_, p_18476_) .append(p_18477_) .append(" total:") .append(p_18478_.selfValue) .append('/') .append(p_18478_.totalValue) .append(" average: ") .append(p_18478_.selfValue / p_18479_) .append('/') .append(p_18478_.totalValue / p_18479_) .append('\n'); p_18478_.children .entrySet() .stream() .sorted(COUNTER_ENTRY_COMPARATOR) .forEach(p_18474_ -> this.appendCounterResults(p_18476_ + 1, p_18474_.getKey(), p_18474_.getValue(), p_18479_, p_18480_)); } private void appendCounters(Map p_18515_, StringBuilder p_18516_, int p_18517_) { p_18515_.forEach((p_18503_, p_18504_) -> { p_18516_.append("-- Counter: ").append(p_18503_).append(" --\n"); this.appendCounterResults(0, "root", p_18504_.children.get("root"), p_18517_, p_18516_); p_18516_.append("\n\n"); }); } @Override public int getTickDuration() { return this.tickDuration; } static class CounterCollector { long selfValue; long totalValue; final Map children = Maps.newHashMap(); public void addValue(Iterator p_18548_, long p_18549_) { this.totalValue += p_18549_; if (!p_18548_.hasNext()) { this.selfValue += p_18549_; } else { this.children.computeIfAbsent(p_18548_.next(), p_18546_ -> new FilledProfileResults.CounterCollector()).addValue(p_18548_, p_18549_); } } } }