Skip to content

Commit fcb9205

Browse files
committed
wip
1 parent 9c75b5c commit fcb9205

File tree

11 files changed

+458
-256
lines changed

11 files changed

+458
-256
lines changed
Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
import { SessionId } from "./common";
5+
6+
7+
// this file contains the IPC commands that are sent by client (like dotnet-trace) to the diagnostic server (like Mono VM in the browser)
8+
// just formatting bytes, no sessions here
9+
10+
export function commandStopTracing (sessionID:SessionId) {
11+
return Uint8Array.from([
12+
...serializeHeader(CommandSetId.EventPipe, EventPipeCommandId.StopTracing, computeMessageByteLength(8)),
13+
...serializeUint64(sessionID),
14+
]);
15+
}
16+
17+
export function createGcHeapDumpCommand () {
18+
return commandCollectTracing2({
19+
circularBufferMB: 256 * 1024 * 1024,
20+
format: 1,
21+
requestRundown: true,
22+
providers: [
23+
{
24+
keywords: [
25+
0x0000_0000,
26+
Keywords.GCHeapSnapshot,
27+
],
28+
logLevel: 5,
29+
provider_name: "Microsoft-Windows-DotNETRuntime",
30+
filter_data: null
31+
}
32+
]
33+
});
34+
}
35+
36+
const enum CommandSetId {
37+
Reserved = 0,
38+
Dump = 1,
39+
EventPipe = 2,
40+
Profiler = 3,
41+
Process = 4,
42+
43+
// replies
44+
Server = 0xFF,
45+
}
46+
47+
const enum Keywords {
48+
None = 0,
49+
//
50+
// Summary:
51+
// Logging when garbage collections and finalization happen.
52+
GC = 1,
53+
//
54+
// Summary:
55+
// Events when GC handles are set or destroyed.
56+
GCHandle = 2,
57+
Binder = 4,
58+
//
59+
// Summary:
60+
// Logging when modules actually get loaded and unloaded.
61+
Loader = 8,
62+
//
63+
// Summary:
64+
// Logging when Just in time (JIT) compilation occurs.
65+
Jit = 0x10,
66+
//
67+
// Summary:
68+
// Logging when precompiled native (NGEN) images are loaded.
69+
NGen = 0x20,
70+
//
71+
// Summary:
72+
// Indicates that on attach or module load , a rundown of all existing methods should
73+
// be done
74+
StartEnumeration = 0x40,
75+
//
76+
// Summary:
77+
// Indicates that on detach or process shutdown, a rundown of all existing methods
78+
// should be done
79+
StopEnumeration = 0x80,
80+
//
81+
// Summary:
82+
// Events associated with validating security restrictions.
83+
Security = 0x400,
84+
//
85+
// Summary:
86+
// Events for logging resource consumption on an app-domain level granularity
87+
AppDomainResourceManagement = 0x800,
88+
//
89+
// Summary:
90+
// Logging of the internal workings of the Just In Time compiler. This is fairly
91+
// verbose. It details decisions about interesting optimization (like inlining and
92+
// tail call)
93+
JitTracing = 0x1000,
94+
//
95+
// Summary:
96+
// Log information about code thunks that transition between managed and unmanaged
97+
// code.
98+
Interop = 0x2000,
99+
//
100+
// Summary:
101+
// Log when lock contention occurs. (Monitor.Enters actually blocks)
102+
Contention = 0x4000,
103+
//
104+
// Summary:
105+
// Log exception processing.
106+
Exception = 0x8000,
107+
//
108+
// Summary:
109+
// Log events associated with the threadpoo, and other threading events.
110+
Threading = 0x10000,
111+
//
112+
// Summary:
113+
// Dump the native to IL mapping of any method that is JIT compiled. (V4.5 runtimes
114+
// and above).
115+
JittedMethodILToNativeMap = 0x20000,
116+
//
117+
// Summary:
118+
// If enabled will suppress the rundown of NGEN events on V4.0 runtime (has no effect
119+
// on Pre-V4.0 runtimes).
120+
OverrideAndSuppressNGenEvents = 0x40000,
121+
//
122+
// Summary:
123+
// Enables the 'BulkType' event
124+
Type = 0x80000,
125+
//
126+
// Summary:
127+
// Enables the events associated with dumping the GC heap
128+
GCHeapDump = 0x100000,
129+
//
130+
// Summary:
131+
// Enables allocation sampling with the 'fast'. Sample to limit to 100 allocations
132+
// per second per type. This is good for most detailed performance investigations.
133+
// Note that this DOES update the allocation path to be slower and only works if
134+
// the process start with this on.
135+
GCSampledObjectAllocationHigh = 0x200000,
136+
//
137+
// Summary:
138+
// Enables events associate with object movement or survival with each GC.
139+
GCHeapSurvivalAndMovement = 0x400000,
140+
//
141+
// Summary:
142+
// Triggers a GC. Can pass a 64 bit value that will be logged with the GC Start
143+
// event so you know which GC you actually triggered.
144+
GCHeapCollect = 0x800000,
145+
//
146+
// Summary:
147+
// Indicates that you want type names looked up and put into the events (not just
148+
// meta-data tokens).
149+
GCHeapAndTypeNames = 0x1000000,
150+
//
151+
// Summary:
152+
// Enables allocation sampling with the 'slow' rate, Sample to limit to 5 allocations
153+
// per second per type. This is reasonable for monitoring. Note that this DOES update
154+
// the allocation path to be slower and only works if the process start with this
155+
// on.
156+
GCSampledObjectAllocationLow = 0x2000000,
157+
//
158+
// Summary:
159+
// Turns on capturing the stack and type of object allocation made by the .NET Runtime.
160+
// This is only supported after V4.5.3 (Late 2014) This can be very verbose and
161+
// you should seriously using GCSampledObjectAllocationHigh instead (and GCSampledObjectAllocationLow
162+
// for production scenarios).
163+
GCAllObjectAllocation = 0x2200000,
164+
//
165+
// Summary:
166+
// This suppresses NGEN events on V4.0 (where you have NGEN PDBs), but not on V2.0
167+
// (which does not know about this bit and also does not have NGEN PDBS).
168+
SupressNGen = 0x40000,
169+
//
170+
// Summary:
171+
// TODO document
172+
PerfTrack = 0x20000000,
173+
//
174+
// Summary:
175+
// Also log the stack trace of events for which this is valuable.
176+
Stack = 0x40000000,
177+
//
178+
// Summary:
179+
// This allows tracing work item transfer events (thread pool enqueue/dequeue/ioenqueue/iodequeue/a.o.)
180+
ThreadTransfer = 0x80000000,
181+
//
182+
// Summary:
183+
// .NET Debugger events
184+
Debugger = 0x100000000,
185+
//
186+
// Summary:
187+
// Events intended for monitoring on an ongoing basis.
188+
Monitoring = 0x200000000,
189+
//
190+
// Summary:
191+
// Events that will dump PDBs of dynamically generated assemblies to the ETW stream.
192+
Codesymbols = 0x400000000,
193+
//
194+
// Summary:
195+
// Events that provide information about compilation.
196+
Compilation = 0x1000000000,
197+
//
198+
// Summary:
199+
// Diagnostic events for diagnosing compilation and pre-compilation features.
200+
CompilationDiagnostic = 0x2000000000,
201+
//
202+
// Summary:
203+
// Diagnostic events for capturing token information for events that express MethodID
204+
MethodDiagnostic = 0x4000000000,
205+
//
206+
// Summary:
207+
// Diagnostic events for diagnosing issues involving the type loader.
208+
TypeDiagnostic = 0x8000000000,
209+
//
210+
// Summary:
211+
// Events for wait handle waits.
212+
WaitHandle = 0x40000000000,
213+
//
214+
// Summary:
215+
// Recommend default flags (good compromise on verbosity).
216+
Default = 0x14C14FCCBD,
217+
//
218+
// Summary:
219+
// What is needed to get symbols for JIT compiled code.
220+
JITSymbols = 0x60098,
221+
//
222+
// Summary:
223+
// This provides the flags commonly needed to take a heap .NET Heap snapshot with
224+
// ETW.
225+
GCHeapSnapshot = 0x1980001
226+
}
227+
228+
const enum EventPipeCommandId {
229+
StopTracing= 1,
230+
CollectTracing= 2,
231+
CollectTracing2= 3,
232+
}
233+
234+
const enum ProcessCommandId {
235+
ProcessInfo= 0,
236+
ResumeRuntime= 1,
237+
ProcessEnvironment= 2,
238+
ProcessInfo2= 4,
239+
}
240+
241+
const enum ServerCommandId {
242+
OK= 0,
243+
Error= 0xFF,
244+
}
245+
246+
function serializeMagic () {
247+
return Uint8Array.from("DOTNET_IPC_V1\0", (c) => c.codePointAt(0) ?? 0);
248+
}
249+
250+
function serializeUint8 (value:number) {
251+
return Uint8Array.from([value]);
252+
}
253+
254+
function serializeUint16 (value:number) {
255+
return new Uint8Array(Uint16Array.from([value]).buffer);
256+
}
257+
258+
function serializeUint32 (value:number) {
259+
return new Uint8Array(Uint32Array.from([value]).buffer);
260+
}
261+
262+
function serializeUint64 (value:[number, number]) {
263+
// value == [hi, lo]
264+
return new Uint8Array(Uint32Array.from([value[1], value[0]]).buffer);
265+
}
266+
267+
function serializeString (value:string|null) {
268+
const message = [];
269+
if (value === null || value === undefined || value === "") {
270+
message.push(...serializeUint32(1));
271+
message.push(...serializeUint16(0));
272+
} else {
273+
const len = value.length;
274+
const hasNul = value[len - 1] === "\0";
275+
message.push(...serializeUint32(len + (hasNul ? 0 : 1)));
276+
for (let i = 0; i < len; i++) {
277+
message.push(...serializeUint16(value.charCodeAt(i)));
278+
}
279+
if (!hasNul) {
280+
message.push(...serializeUint16(0));
281+
}
282+
}
283+
return message;
284+
}
285+
286+
function computeStringByteLength (s:string|null) {
287+
if (s === undefined || s === null || s === "")
288+
return 4 + 2; // just length of empty zero terminated string
289+
return 4 + 2 * s.length + 2; // length + UTF16 + null
290+
}
291+
292+
function computeMessageByteLength (payloadLength:number) {
293+
const fullHeaderSize = 14 + 2 // magic, len
294+
+ 1 + 1 // commandSet, command
295+
+ 2; // reserved ;
296+
return fullHeaderSize + payloadLength;
297+
}
298+
299+
function serializeHeader (commandSet:CommandSetId, command:ServerCommandId|EventPipeCommandId|ProcessCommandId, len:number) {
300+
return Uint8Array.from([
301+
...serializeMagic(),
302+
...serializeUint16(len),
303+
...serializeUint8(commandSet),
304+
...serializeUint8(command),
305+
...serializeUint16(0), // reserved*/
306+
]);
307+
}
308+
309+
function computeCollectTracing2PayloadByteLength (payload2:PayloadV2) {
310+
let len = 0;
311+
len += 4; // circularBufferMB
312+
len += 4; // format
313+
len += 1; // requestRundown
314+
len += 4; // providers length
315+
for (const provider of payload2.providers) {
316+
len += 8; // keywords
317+
len += 4; // level
318+
len += computeStringByteLength(provider.provider_name);
319+
len += computeStringByteLength(provider.filter_data);
320+
}
321+
return len;
322+
}
323+
324+
export function commandCollectTracing2 (payload2:PayloadV2) {
325+
const payloadLength = computeCollectTracing2PayloadByteLength(payload2);
326+
const messageLength = computeMessageByteLength(payloadLength);
327+
const message = [
328+
...serializeHeader(CommandSetId.EventPipe, EventPipeCommandId.CollectTracing2, messageLength),
329+
...serializeUint32(payload2.circularBufferMB),
330+
...serializeUint32(payload2.format),
331+
...serializeUint8(payload2.requestRundown ? 1 : 0),
332+
...serializeUint32(payload2.providers.length),
333+
];
334+
for (const provider of payload2.providers) {
335+
message.push(...serializeUint64(provider.keywords));
336+
message.push(...serializeUint32(provider.logLevel));
337+
message.push(...serializeString(provider.provider_name));
338+
message.push(...serializeString(provider.filter_data));
339+
}
340+
return Uint8Array.from(message);
341+
}
342+
343+
export type ProviderV2 ={
344+
keywords: [ 0, Keywords, ],
345+
logLevel: number,
346+
provider_name: string,
347+
filter_data: string|null
348+
}
349+
350+
export type PayloadV2 = {
351+
circularBufferMB: number,
352+
format: number,
353+
requestRundown: boolean,
354+
providers: ProviderV2[]
355+
}

0 commit comments

Comments
 (0)