1+ #pragma author WerWolv
2+ #pragma description Java HPROF Profiler Data Format
3+ #pragma endian big
4+ #pragma magic [ "JAVA PROFILE" ] @ 0x00
5+
6+ #pragma array_limit 0
7+ #pragma pattern_limit 0
8+
9+ import std.mem;
10+ import std.io;
11+ import std.sys;
12+ import std.core;
13+
14+ enum Tag : u8 {
15+ STRING_IN_UTF8 = 0x01,
16+ LOAD_CLASS = 0x02,
17+ UNLOAD_CLASS = 0x03,
18+ STACK_FRAME = 0x04,
19+ STACK_TRACE = 0x05,
20+ ALLOC_SITES = 0x06,
21+ HEAP_SUMMARY = 0x07,
22+ START_THREAD = 0x0A,
23+ END_THREAD = 0x0B,
24+ HEAP_DUMP = 0x0C,
25+ HEAP_DUMP_SEGMENT = 0x1C,
26+ HEAP_DUMP_END = 0x2C,
27+ CPU_SAMPLES = 0x0D,
28+ CONTROL_SETTINGS = 0x0E,
29+ };
30+
31+ u32 id_size = 0;
32+ struct ID {
33+ match (id_size) {
34+ (1): u8 value;
35+ (2): u16 value;
36+ (4): u32 value;
37+ (8): u64 value;
38+ (_): std::error("Invalid ID size");
39+ }
40+ } [[sealed, format("format_id")]];
41+
42+ fn format_id(ref auto id) {
43+ return std::format("0x{:0{}X}", id.value, id_size * 2);
44+ };
45+
46+ struct StringInUTF8 {
47+ ID id;
48+ char string[parent.length - sizeof(id)];
49+ };
50+
51+ struct LoadClass {
52+ u32 classSerialNumber;
53+ ID classObjectId;
54+ u32 stackTraceSerialNumber;
55+ ID classNameStringID;
56+ };
57+
58+ struct StackFrame {
59+ ID stackFrameId;
60+ ID methodNameStringId;
61+ ID methodSignatureStringId;
62+ ID sourceFileNameStringId;
63+ u32 classSerialNumber;
64+ u32 lineInformation;
65+ };
66+
67+ struct StackTrace {
68+ u32 stackTraceSerialNumber;
69+ u32 threadSerialNumber;
70+ u32 numberOfFrames;
71+ ID stackFrameIds[while(!std::mem::reached(addressof(this) + parent.length))];
72+ };
73+
74+ enum SubTag : u8 {
75+ RootUnknown = 0xFF,
76+ RootJNIGlobal = 0x01,
77+ RootJNILocal = 0x02,
78+ RootJavaFrame = 0x03,
79+ RootNativeStack = 0x04,
80+ RootStickyClass = 0x05,
81+ RootThreadBlock = 0x06,
82+ RootMonitorUsed = 0x07,
83+ RootThreadObject = 0x08,
84+ ClassDump = 0x20,
85+ InstanceDump = 0x21,
86+ ObjectArrayDump = 0x22,
87+ PrimitiveArrayDump = 0x23
88+ };
89+
90+ enum EntryType : u8 {
91+ Object = 2,
92+ Boolean = 4,
93+ Char = 5,
94+ Float = 6,
95+ Double = 7,
96+ Byte = 8,
97+ Short = 9,
98+ Int = 10,
99+ Long = 11
100+ };
101+
102+ struct BasicType<auto Tag> {
103+ match (Tag) {
104+ (EntryType::Object): ID value;
105+ (EntryType::Boolean): bool value;
106+ (EntryType::Char): { padding[1]; char value; }
107+ (EntryType::Float): float value;
108+ (EntryType::Double): double value;
109+ (EntryType::Byte): u8 value;
110+ (EntryType::Short): u16 value;
111+ (EntryType::Int): u32 value;
112+ (EntryType::Long): u64 value;
113+ (_): std::error("Invalid BasicType type");
114+ }
115+ } [[sealed, format("format_basic_type")]];
116+
117+ fn format_basic_type(ref auto basicType) {
118+ return std::format("{}", basicType.value);
119+ };
120+
121+ struct ConstantPoolEntry {
122+ u16 constantPoolIndex;
123+ EntryType type;
124+ BasicType<type> value;
125+ };
126+
127+ struct ConstantPoolArray {
128+ u16 size;
129+ ConstantPoolEntry entries[size];
130+ };
131+
132+ struct StaticFieldEntry {
133+ ID staticFieldNameStringId;
134+ EntryType type;
135+ BasicType<type> value;
136+ };
137+
138+ struct StaicFieldArray {
139+ u16 size;
140+ StaticFieldEntry entries[size];
141+ };
142+
143+ struct InstanceField {
144+ ID fieldNameStringId;
145+ EntryType fieldType;
146+ };
147+
148+ struct InstanceFieldsArray {
149+ u16 size;
150+ InstanceField instanceFields[size];
151+ };
152+
153+ struct HeapDump {
154+ SubTag subTag;
155+ match (subTag) {
156+ (SubTag::RootUnknown): {
157+ ID objectId;
158+ }
159+ (SubTag::RootJNIGlobal): {
160+ ID objectId;
161+ ID jniGlobalRefId;
162+ }
163+ (SubTag::RootJNILocal): {
164+ ID objectId;
165+ u32 threadSerialNumber;
166+ u32 frameNumberInStackTrace;
167+ }
168+ (SubTag::RootJavaFrame): {
169+ ID objectId;
170+ u32 threadSerialNumber;
171+ u32 frameNumberInStackTrace;
172+ }
173+ (SubTag::RootNativeStack): {
174+ ID objectId;
175+ u32 threadSerialNumber;
176+ }
177+ (SubTag::RootStickyClass): {
178+ ID objectId;
179+ }
180+ (SubTag::RootThreadBlock): {
181+ ID objectId;
182+ u32 threadSerialNumber;
183+ }
184+ (SubTag::RootMonitorUsed): {
185+ ID objectId;
186+ }
187+ (SubTag::RootThreadObject): {
188+ ID threadObjectId;
189+ u32 threadSerialNumber;
190+ u32 stackTraceSerialNumber;
191+ }
192+ (SubTag::ClassDump): {
193+ ID classObjectId;
194+ u32 stackTraceSerialNumber;
195+ ID superClassObjectId;
196+ ID classLoaderObjectId;
197+ ID signersObjectId;
198+ ID protectionDomainObjectId;
199+ ID reserved[2];
200+ u32 instanceSize;
201+ ConstantPoolArray constantPool;
202+ StaicFieldArray staticFields;
203+ InstanceFieldsArray instanceFields;
204+ }
205+ (SubTag::InstanceDump): {
206+ ID objectId;
207+ u32 stackTraceSerialNumber;
208+ ID classObjectId;
209+ u32 numBytes;
210+ std::mem::Bytes<numBytes> instanceFieldValues;
211+ }
212+ (SubTag::ObjectArrayDump): {
213+ ID arrayObjectId;
214+ u32 stackTraceSerialNumber;
215+ u32 numberOfElements;
216+ ID arrayClassObjectId;
217+ ID elements[numberOfElements];
218+ }
219+ (SubTag::PrimitiveArrayDump): {
220+ ID arrayObjectId;
221+ u32 stackTraceSerialNumber;
222+ u32 numberOfElements;
223+ EntryType type;
224+ BasicType<type> value[numberOfElements];
225+ }
226+ (_): std::error(std::format("Heap Dump Sub Tag {:02X} at 0x{:08X} {}", u32(subTag), $, std::core::array_index()));
227+ }
228+
229+ if ($ - addressof(parent.length) + sizeof(parent.length) >= parent.length)
230+ break;
231+
232+ u8 endTag [[no_unique_address, hidden]];
233+ if (endTag == 0x2C) {
234+ padding[1];
235+ break;
236+ }
237+ };
238+
239+ struct UnloadClass {
240+ u32 classSerialNumber;
241+ };
242+
243+ bitfield AllocSitesFlags {
244+ completeInsteadOfIncremental : 1;
245+ sortedByLineInsteadOfAllocation : 1;
246+ forceGc : 1;
247+ padding : 13;
248+ };
249+
250+ struct AllocSite {
251+ bool arrayIndicator;
252+ u32 classSerialNumber;
253+ u32 stackTraceSerialNumber;
254+ u32 numberOfLiveBytes;
255+ u32 numberOfLiveInstances;
256+ u32 numberOfBytesAllocated;
257+ u32 numberOfInstancesAllocated;
258+ };
259+
260+ struct AllocSites {
261+ AllocSitesFlags flags;
262+ float cutoffRatio;
263+ u32 totalLiveBytes;
264+ u32 totalLiveInstances;
265+ u64 totalBytesAllocated;
266+ u64 totalInstancesAllocated;
267+ u32 numSites;
268+ AllocSite sites[numSites];
269+ };
270+
271+ struct HeapSummary {
272+ u32 totalLiveBytes;
273+ u32 totalLiveInstances;
274+ u64 totalBytesAllocated;
275+ u64 totalInstancesAllocated;
276+ };
277+
278+ struct StartThread {
279+ u32 threadSerialNumber;
280+ ID threadObjectId;
281+ u32 stackTraceSerialNumber;
282+ ID threadNameStringId;
283+ ID threadGroupNameId;
284+ ID threadParentGroupNameId;
285+ };
286+
287+ struct EndThread {
288+ u32 threadSerialNumber;
289+ };
290+
291+ struct Sample {
292+ u32 numberOfSamples;
293+ u32 stackTraceSerialNumber;
294+ };
295+
296+ struct CpuSamples {
297+ u32 numSamples;
298+ Sample samples[numSamples];
299+ };
300+
301+ bitfield ControlSettingsFlags {
302+ allocTraces : 1;
303+ cpuSampling : 1;
304+ padding : 30;
305+ };
306+
307+ struct ControlSettings {
308+ ControlSettingsFlags flags;
309+ u16 stackTraceDepth;
310+ };
311+
312+ struct Record {
313+ Tag tag;
314+ u32 time;
315+ u32 length;
316+
317+ match (tag) {
318+ (Tag::STRING_IN_UTF8):
319+ StringInUTF8 body;
320+ (Tag::LOAD_CLASS):
321+ LoadClass body;
322+ (Tag::UNLOAD_CLASS):
323+ UnloadClass body;
324+ (Tag::STACK_FRAME):
325+ StackFrame body;
326+ (Tag::STACK_TRACE):
327+ StackTrace body;
328+ (Tag::ALLOC_SITES):
329+ AllocSites body;
330+ (Tag::HEAP_SUMMARY):
331+ HeapSummary body;
332+ (Tag::START_THREAD):
333+ StartThread body;
334+ (Tag::END_THREAD):
335+ EndThread body;
336+ (Tag::HEAP_DUMP | Tag::HEAP_DUMP_SEGMENT):
337+ HeapDump heapDumps[while(true)];
338+ (Tag::HEAP_DUMP_END):
339+ std::error("HEAP_DUMP_END Tag without previous HEAP_DUMP or HEAP_DUMP_SEGMENT Tag");
340+ (Tag::CPU_SAMPLES):
341+ CpuSamples body;
342+ (Tag::CONTROL_SETTINGS):
343+ ControlSettings body;
344+ (_):
345+ std::error(std::format("Unknown record type {:02X} at address 0x{:08X}, index {}", u8(tag), addressof(tag), std::core::array_index()));
346+ }
347+ };
348+
349+ struct Header {
350+ char format_name[];
351+ u32 identifier_size;
352+ id_size = identifier_size;
353+ u32 timestamp_high;
354+ u32 time_stamp_low;
355+ };
356+
357+ struct HPROF {
358+ Header header;
359+ Record records[while(!std::mem::eof())];
360+ };
361+
362+ HPROF hprof @ 0x00;
0 commit comments