|
| 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