/
PacketProfiler.java
163 lines (149 loc) · 4.93 KB
/
PacketProfiler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package nallar.tickthreading.minecraft.profiling;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.base.Functions;
import com.google.common.collect.Ordering;
import nallar.tickthreading.minecraft.commands.Command;
import nallar.tickthreading.util.MappingUtil;
import nallar.tickthreading.util.TableFormatter;
import net.minecraft.command.ICommandSender;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.Packet250CustomPayload;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
public class PacketProfiler {
private static boolean profiling = false;
private static final Map<String, AtomicInteger> size = new NonBlockingHashMap<String, AtomicInteger>();
private static final Map<String, AtomicInteger> count = new NonBlockingHashMap<String, AtomicInteger>();
public static synchronized boolean startProfiling(final ICommandSender commandSender, final int time) {
if (profiling) {
Command.sendChat(commandSender, "Someone else is already profiling packets.");
return false;
}
profiling = true;
Command.sendChat(commandSender, "Profiling packets for " + time + " seconds.");
new Thread() {
@Override
public void run() {
try {
Thread.sleep(time * 1000);
} catch (InterruptedException ignored) {
}
Command.sendChat(commandSender, writeStats(new TableFormatter(commandSender)).toString());
synchronized (PacketProfiler.class) {
size.clear();
count.clear();
profiling = false;
}
}
}.start();
return true;
}
private static <T> List<T> sortedKeys(Map<T, ? extends Comparable<?>> map, int elements) {
List<T> list = Ordering.natural().reverse().onResultOf(Functions.forMap(map)).immutableSortedCopy(map.keySet());
return list.size() > elements ? list.subList(0, elements) : list;
}
private static TableFormatter writeStats(final TableFormatter tf) {
return writeStats(tf, 9);
}
private static TableFormatter writeStats(final TableFormatter tf, int elements) {
Map<String, Integer> count = new HashMap<String, Integer>();
for (Map.Entry<String, AtomicInteger> entry : PacketProfiler.count.entrySet()) {
count.put(entry.getKey(), entry.getValue().get());
}
Map<String, Integer> size = new HashMap<String, Integer>();
for (Map.Entry<String, AtomicInteger> entry : PacketProfiler.size.entrySet()) {
size.put(entry.getKey(), entry.getValue().get());
}
tf
.heading("Packet")
.heading("Count")
.heading("Size");
final List<String> sortedIdsByCount = sortedKeys(count, elements);
for (String id : sortedIdsByCount) {
tf
.row(getName(id))
.row(count.get(id))
.row(humanReadableByteCount(size.get(id)));
}
tf.finishTable();
tf.sb.append('\n');
tf
.heading("Packet")
.heading("Count")
.heading("Size");
final List<String> sortedIdsBySize = sortedKeys(size, elements);
for (String id : sortedIdsBySize) {
tf
.row(getName(id))
.row(count.get(id))
.row(humanReadableByteCount(size.get(id)));
}
tf.finishTable();
return tf;
}
private static String getName(String name) {
int id;
try {
id = Integer.parseInt(name);
} catch (NumberFormatException ignored) {
return name;
}
return MappingUtil.debobfuscate(((Class) Packet.packetIdToClassMap.lookup(id)).getName()).replace("net.minecraft.network.packet.Packet", "");
}
public static void record(final Packet packet) {
if (!profiling) {
return;
}
String id;
int size;
if (packet instanceof Packet250CustomPayload) {
Packet250CustomPayload packet250CustomPayload = (Packet250CustomPayload) packet;
byte[] data = packet250CustomPayload.data;
size = data == null ? 0 : data.length;
id = packet250CustomPayload.channel + (size > 0 ? Byte.toString(data[0]) : Byte.MIN_VALUE);
} else {
id = String.valueOf(packet.getPacketId());
size = packet.getPacketSize();
}
getCount(id).getAndIncrement();
getSize(id).addAndGet(size);
}
private static AtomicInteger getCount(String id) {
AtomicInteger t = count.get(id);
if (t == null) {
synchronized (count) {
t = count.get(id);
if (t == null) {
t = new AtomicInteger();
count.put(id, t);
}
}
}
return t;
}
private static AtomicInteger getSize(String id) {
AtomicInteger t = size.get(id);
if (t == null) {
synchronized (size) {
t = size.get(id);
if (t == null) {
t = new AtomicInteger();
size.put(id, t);
}
}
}
return t;
}
// http://stackoverflow.com/a/3758880/250076
public static String humanReadableByteCount(int bytes) {
int unit = 1024;
if (bytes < unit) {
return bytes + " B";
}
int exp = (int) (Math.log(bytes) / Math.log(unit));
char pre = ("KMGTPE").charAt(exp - 1);
return String.format("%.1f%cB", bytes / Math.pow(unit, exp), pre);
}
}