This spec describes the IPC Protocol to be used for communicating with the dotnet core runtime's Diagnostics Server from an external client over a platform-specific transport, e.g., Unix Domain Sockets.
The protocol will use the following names for various constructs and behaviors defined in this spec:
- Diagnostic IPC Protocol: The protocol defined in this spec
- Diagnostic Server: The server in the runtime that receives/sends Diagnostic IPC Procotol communication.
- Commands: The functionality being invoked in the runtime that communicates over the Diagnostic IPC Protocol, e.g., "Start an EventPipe stream". These are encoded as a
command_set
and acommand_id
. - Flow: A sequence of interactions making up communication with the Diagnostics Server
- Pipe: The duplex communication construct this protocol is communicated over. This is a Unix Domain Socket on *nix systems and a Named Pipe on Windows.
- IPC Message: The base unit of communication over the Diagnostic IPC Protocol. Is made up of a Header and a Payload.
- Header: A struct containing a magic version, the size, a command, and metadata.
- Payload: An opaque chunk of data that is Command specific.
- Optional Continuation: The reuse of the pipe for application specific communication. This communication does not need to adhere to any requirements listed in this spec, e.g., this could be a stream of custom encoded data that is Command specific.
All communication with the Diagnostic Server will begin with a Diagnostic IPC Message sent from the client to the server. The server will respond with a Diagnostic IPC Message. After this, the client and runtime may reuse the Pipe for any Command specific communication which is referred to as an Optional Continuation.
runtime <- client : [ Header ][ Payload ]
runtime -> client : [ Header ][ Payload ]
optional:
runtime <-> client : [ Optional Continuation ]
connection closed
Example flow for EventPipe:
runtime <- client : [ magic; size; EventPipe CollectTracing ][ stream config struct ] <- Diagnostic IPC Message
runtime -> client : [ magic; size; Server OK ][ sessionId ] <- Diagnostic IPC Message
runtime -> client : [ stream of nettrace data ] <- Optional Continuation
// stop message is sent on another connection
connection closed
The protocol will be communicated over a platform-specific transport. On Unix/Linux based platforms, a Unix Domain Socket will be used, and on Windows, a Named Pipe will be used.
Unix Domain Sockets (MacOS and *nix):
The socket is placed in one of two places:
- The directory specified in
$TMPDIR
/tmp
if$TMPDIR
is undefined/empty
In order to ensure filename uniqueness, a disambiguation key
is generated. On Mac and NetBSD, this is the process start time encoded as the number of seconds since UNIX epoch time. If /proc/$PID/stat
is available (all other *nix platforms), then the process start time encoded as jiffies since boot time is used.
NOTE: If the target application is running inside an application sandbox on MacOS, the transport will be placed in the Application Group container directory. This is a convention for all sandboxed applications on MacOS.
socket name:
dotnet-diagnostic-{%d:PID}-{%llu:disambiguation key}-socket
Named Pipes (Windows):
\\.\pipe\dotnet-diagnostic-{%d:PID}
Diagnostic IPC Messages are the base unit of communication with the Diagnostic Server. A Diagnostic IPC Message contains a Header and Payload (described in following sections).
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ... | size - 1 | size |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
header | payload | |||||||||||||||||||||||||
magic | size | command_set | command_id | reserved | payload |
The simplest Diagnostic IPC Message will contain a Header and an empty Payload and therefore only be 20 bytes long.
For example, this IPC Message is the generic OK message which has an empty Payload:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
magic | size | command | reserved | ||||||||||||||||
"DOTNET_IPC_V1" | 20 | 0xFF | 0x00 | 0x0000 |
Every Diagnostic IPC Message will start with a header and every header will:
- start with a magic version number and a size
sizeof(IpcHeader) == 20
- encode numbers little-endian
- account for the size of the payload in the
size
value, i.e.,IpcHeader.size == sizeof(IpcHeader) + PayloadStruct.GetSize()
// size = 14 + 2 + 1 + 1 + 2 = 20 bytes
struct IpcHeader
{
uint8_t[14] magic = "DOTNET_IPC_V1";
uint16_t size; // size of packet = size of header + payload
uint8_t command_set; // combined with command_id is the Command to invoke
uint8_t command_id; // combined with command_set is the Command to invoke
uint16_t reserved; // for potential future use
};
The reserved
field is reserved for future use. It is unused in DOTNET_IPC_V1
and must be 0x0000.
Payloads are Command specific data encoded into a Diagnostic IPC Message. The size of the payload is implicitly encoded in the Header's size
field as PayloadSize = header.size - sizeof(struct IpcHeader)
. A Payload may be 0 bytes long if it empty. The encoding of data in the Payload is Command specific.
Payloads are either encoded as fixed size structures that can be memcpy
'ed , or:
X, Y, Z
means encode bytes forX
followed by bytes forY
followed by bytes forZ
uint
= 4 little endian bytesulong
= 8 little endian byteswchar
= 2 little endian bytes, UTF16 encodingarray<T>
= uint length, length # ofT
sstring
= (array<wchar>
where the lastwchar
must =0
) or (length =0
)
As an example, the CollectTracing command to EventPipe (explained below) encodes its Payload as:
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 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Header | Payload | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
magic | size | command | reserved | circularBufferMB | outputPath Length | outputPath String | n Providers | Keywords | logLevel | provider_name length | provider_name string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"DOTNET_IPC_V1" | 78 | 0x0202 | 0x0000 | 250 | 16 | "/tmp/foo.nettrace" | 1 | 100 | 2 | 14 | "MyEventSource" |
Where 0x0202
is the Command to start streaming with EventPipe.
Commands are a command_set
and a command_id
. A command_set
is analogous to a namespace for command_id
s. The typical grouping is by service running on the Diagnostic Server, e.g., there is a command_set
for EventPipe. This allows multiple services to have the same command_id
s without clashing. The combination of a command_set
and a command_id
encodes the Command being invoked on the Diagnostic Server.
The current set of command_set
s and command_id
s are listed below:
enum class CommandSet : uint8_t
{
// reserved = 0x00,
Dump = 0x01,
EventPipe = 0x02,
Profiler = 0x03,
Process = 0x04,
// future
Server = 0xFF,
};
enum class ServerCommandId : uint8_t
{
OK = 0x00,
Error = 0xFF,
};
enum class EventPipeCommandId : uint8_t
{
// reserved = 0x00,
StopTracing = 0x01, // stop a given session
CollectTracing = 0x02, // create/start a given session
CollectTracing2 = 0x03, // create/start a given session with/without rundown
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
}
See: EventPipe Commands
enum class DumpCommandId : uint8_t
{
// reserved = 0x00,
CreateCoreDump = 0x01,
// future
}
See: Dump Commands
enum class ProfilerCommandId : uint8_t
{
// reserved = 0x00,
AttachProfiler = 0x01,
// future
}
See: Profiler Commands
enum class ProcessCommandId : uint8_t
{
ProcessInfo = 0x00,
ResumeRuntime = 0x01,
ProcessEnvironment = 0x02,
ProcessInfo2 = 0x04,
EnablePerfMap = 0x05,
DisablePerfMap = 0x06,
ApplyStartupHook = 0x07
ProcessInfo3 = 0x08,
// future
}
See: Process Commands
Commands may use the generic { magic="DOTNET_IPC_V1"; size=20; command_set=0xFF (Server); command_id=0x00 (OK); reserved = 0x0000; }
to indicate success rather than having a command specific success command_id
.
For example, the Command to start a stream session with EventPipe would be 0x0202
made up of 0x02
(the command_set
for EventPipe) and 0x02
(the command_id
for CollectTracing).
enum class EventPipeCommandId : uint8_t
{
// reserved = 0x00,
StopTracing = 0x01, // stop a given session
CollectTracing = 0x02, // create/start a given session
CollectTracing2 = 0x03, // create/start a given session with/without rundown
CollectTracing3 = 0x04, // create/start a given session with/without collecting stacks
CollectTracing4 = 0x05, // create/start a given session with specific rundown keyword
}
EventPipe Payloads are encoded with the following rules:
X, Y, Z
means encode bytes forX
followed by bytes forY
followed by bytes forZ
uint
= 4 little endian bytesulong
= 8 little endian byteswchar
= 2 little endian bytes, UTF16 encodingbyte
= 1 unsigned little endian bytearray<T>
= uint length, length # ofT
sstring
= (array<wchar>
where the lastwchar
must =0
) or (length =0
)
Command Code: 0x0202
The CollectTracing
Command is used to start a streaming session of event data. The runtime will attempt to start a session and respond with a success message with a payload of the sessionId
. The event data is streamed in the nettrace
format. The stream begins after the response Message from the runtime to the client. The client is expected to continue to listen on the transport until the connection is closed.
In the event there is an error, the runtime will attempt to send an error message and subsequently close the connection.
The client is expected to send a StopTracing
command to the runtime in order to stop the stream, as there is a "run down" at the end of a stream session that transmits additional metadata.
If the stream is stopped prematurely due to a client or server error, the nettrace
file generated will be incomplete and should be considered corrupted.
Header: { Magic; Size; 0x0202; 0x0000 }
uint circularBufferMB
: The size of the circular buffer used for buffering event data while streaminguint format
: 0 for the legacy NetPerf format and 1 for the NetTrace formatarray<provider_config> providers
: The providers to turn on for the streaming session
A provider_config
is composed of the following data:
ulong keywords
: The keywords to turn on with this providersuint logLevel
: The level of information to turn onstring provider_name
: The name of the providerstring filter_data
(optional): Filter information
see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level.
Header: { Magic; 28; 0xFF00; 0x0000; }
CollectTracing
returns:
ulong sessionId
: the ID for the stream session starting on the current connection
Input:
Payload
{
uint circularBufferMB,
uint format,
array<provider_config> providers
}
provider_config
{
ulong keywords,
uint logLevel,
string provider_name,
string filter_data (optional)
}
Returns:
Payload
{
ulong sessionId
}
Followed by an Optional Continuation of a nettrace
format stream of events.
Command Code: 0x0203
The CollectTracing2
command is an extension of the CollectTracing
command - its behavior is the same as CollectTracing
command, except that it has another field that lets you specify whether rundown events should be fired by the runtime.
Header: { Magic; Size; 0x0203; 0x0000 }
uint circularBufferMB
: The size of the circular buffer used for buffering event data while streaminguint format
: 0 for the legacy NetPerf format and 1 for the NetTrace formatbool requestRundown
: Indicates whether rundown should be fired by the runtime.array<provider_config> providers
: The providers to turn on for the streaming session
A provider_config
is composed of the following data:
ulong keywords
: The keywords to turn on with this providersuint logLevel
: The level of information to turn onstring provider_name
: The name of the providerstring filter_data
(optional): Filter information
see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level.
Header: { Magic; 28; 0xFF00; 0x0000; }
CollectTracing2
returns:
ulong sessionId
: the ID for the stream session starting on the current connection
Input:
Payload
{
uint circularBufferMB,
uint format,
bool requestRundown,
array<provider_config> providers
}
provider_config
{
ulong keywords,
uint logLevel,
string provider_name,
string filter_data (optional)
}
Returns:
Payload
{
ulong sessionId
}
Followed by an Optional Continuation of a nettrace
format stream of events.
Command Code: 0x0204
The CollectTracing3
command is an extension of the CollectTracing2
command - its behavior is the same as CollectTracing2
command, except that it has another field that lets you specify whether the stackwalk should be made for each event.
Header: { Magic; Size; 0x0203; 0x0000 }
uint circularBufferMB
: The size of the circular buffer used for buffering event data while streaminguint format
: 0 for the legacy NetPerf format and 1 for the NetTrace formatbool requestRundown
: Indicates whether rundown should be fired by the runtime.bool requestStackwalk
: Indicates whether stacktrace information should be recorded.array<provider_config> providers
: The providers to turn on for the streaming session
A provider_config
is composed of the following data:
ulong keywords
: The keywords to turn on with this providersuint logLevel
: The level of information to turn onstring provider_name
: The name of the providerstring filter_data
(optional): Filter information
see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level.
Header: { Magic; 28; 0xFF00; 0x0000; }
CollectTracing3
returns:
ulong sessionId
: the ID for the stream session starting on the current connection
Input:
Payload
{
uint circularBufferMB,
uint format,
bool requestRundown,
bool requestStackwalk,
array<provider_config> providers
}
provider_config
{
ulong keywords,
uint logLevel,
string provider_name,
string filter_data (optional)
}
Returns:
Payload
{
ulong sessionId
}
Followed by an Optional Continuation of a nettrace
format stream of events.
Command Code: 0x0205
The CollectTracing4
command is an extension of the CollectTracing3
command - its behavior is the same as CollectTracing3
command, except the requestRundown field is replaced by the rundownKeyword field to allow customizing the set of rundown events to be fired.
A rundown keyword of 0x80020139
has the equivalent behavior as CollectTracing3
with requestRundown=true
and rundown keyword of 0
has the equivalent behavior as requestRundown=false
.
Note available for .NET 9.0 and later.
Header: { Magic; Size; 0x0205; 0x0000 }
uint circularBufferMB
: The size of the circular buffer used for buffering event data while streaminguint format
: 0 for the legacy NetPerf format and 1 for the NetTrace formatulong rundownKeyword
: Indicates the keyword for the rundown providerarray<provider_config> providers
: The providers to turn on for the streaming session
A provider_config
is composed of the following data:
ulong keywords
: The keywords to turn on with this providersuint logLevel
: The level of information to turn onstring provider_name
: The name of the providerstring filter_data
(optional): Filter information
see ETW documentation for a more detailed explanation of Keywords, Filters, and Log Level.
Header: { Magic; 28; 0xFF00; 0x0000; }
CollectTracing4
returns:
ulong sessionId
: the ID for the stream session starting on the current connection
Input:
Payload
{
uint circularBufferMB,
uint format,
ulong rundownKeyword
array<provider_config> providers
}
provider_config
{
ulong keywords,
uint logLevel,
string provider_name,
string filter_data (optional)
}
Returns:
Payload
{
ulong sessionId
}
Followed by an Optional Continuation of a nettrace
format stream of events.
Command Code: 0x0201
The StopTracing
command is used to stop a specific streaming session. Clients are expected to use this command to stop streaming sessions started with CollectStreaming
.
Header: { Magic; 28; 0x0201; 0x0000 }
ulong sessionId
: The ID for the streaming session to stop
Header: { Magic; 28; 0xFF00; 0x0000 }
ulong sessionId
: the ID for the streaming session that was stopped
Inputs:
Payload
{
ulong sessionId
}
Returns:
Payload
{
ulong sessionId
}
Command Code: 0x0101
The CreateCoreDump
command is used to instruct the runtime to generate a core dump of the process. The command will keep the connection open while the dump is generated and then respond with a message containing an HRESULT
indicating success or failure.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0101; 0x0000 }
string dumpName
: The name of the dump generated.uint dumpType
: A value between 1 and 4 inclusive that indicates the type of dump to take- Normal = 1,
- WithHeap = 2,
- Triage = 3,
- Full = 4
uint diagnostics
: If set to 1, log to console the dump generation diagnostics0
or1
for on or off
Header: { Magic; 28; 0xFF00; 0x0000; }
CreateCoreDump
returns:
int32 hresult
: The result of creating the core dump (0
indicates success)
Input:
Payload
{
string dumpName,
uint dumpType,
uint diagnostics
}
Returns:
Payload
{
int32 hresult
}
Command Code: 0x0301
The AttachProfiler
command is used to attach a profiler to the runtime. The command will keep the connection open while the profiler is being attached and then respond with a message containing an HRESULT
indicating success or failure.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0301; 0x0000 }
uint32 attachTimeout
: The timeout for attaching to the profiler (in milliseconds)CLSID profilerGuid
: The GUID associated with the profilerstring profilerPath
: Location of the profilerarray<byte> clientData
: The data being provided to the profiler
Where a CLSID
is a fixed size struct consisting of:
uint x
byte s1
byte s2
byte[8] c
Header: { Magic; 28; 0xFF00; 0x0000; }
AttachProfiler
returns:
int32 hresult
: The result of attaching the profiler (0
indicates success)
Input:
Payload
{
uint32 dwAttachTimeout
CLSID profilerGuid
string profilerPath
uint32 clientDataSize
array<byte> pClientData
}
Returns:
Payload
{
int32 hresult
}
Available since .NET 5.0
Command Code: 0x0400
The ProcessInfo
command queries the runtime for some basic information about the process.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0400; 0x0000 }
There is no payload.
Header: { Magic; size; 0xFF00; 0x0000; }
Payload:
int64 processId
: the process id in the process's PID-spaceGUID runtimeCookie
: a 128-bit GUID that should be unique across PID-spacesstring commandLine
: the command line that invoked the process- Windows: will be the same as the output of
GetCommandLineW
- Non-Windows: will be the fully qualified path of the executable in
argv[0]
followed by all arguments as the appear inargv
separated by spaces, i.e.,/full/path/to/argv[0] argv[1] argv[2] ...
- Windows: will be the same as the output of
string OS
: the operating system that the process is running on- macOS =>
"macOS"
- Windows =>
"Windows"
- Linux =>
"Linux"
- other =>
"Unknown"
- macOS =>
string arch
: the architecture of the process- 32-bit =>
"x86"
- 64-bit =>
"x64"
- ARM32 =>
"arm32"
- ARM64 =>
"arm64"
- Other =>
"Unknown"
- 32-bit =>
Returns:
struct Payload
{
uint64_t ProcessId;
LPCWSTR CommandLine;
LPCWSTR OS;
LPCWSTR Arch;
GUID RuntimeCookie;
}
Command Code: 0x0401
If the target .NET application has been configured Diagnostic Ports configured to suspend with DOTNET_DiagnosticPorts
or DOTNET_DefaultDiagnosticPortSuspend
has been set to 1
(0
is the default value), then the runtime will pause during EEStartupHelper
in ceemain.cpp
and wait for an event to be set. (See Diagnostic Ports for more details)
The ResumeRuntime
command sets the necessary event to resume runtime startup. If the .NET application has not been configured to with Diagnostics Monitor Address or the runtime has already been resumed, this command is a no-op.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0401; 0x0000 }
There is no payload.
Header: { Magic; size; 0xFF00; 0x0000; }
There is no payload.
Command Code: 0x0402
The ProcessEnvironment
command queries the runtime for its environment block.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0402; 0x0000 }
There is no payload.
Header: { Magic; size; 0xFF00; 0x0000; }
Payload:
uint32_t nIncomingBytes
: the number of bytes to expect in the continuation streamuint16_t future
: unused
Continuation:
Array<Array<WCHAR>> environmentBlock
: The environment block written as a length prefixed array of length prefixed arrays ofWCHAR
.
Note: it is valid for nIncomingBytes
to be 4
and the continuation to simply contain the value 0
.
Returns:
struct Payload
{
uint32_t nIncomingBytes;
uint16_t future;
}
Available since .NET 6.0
Command Code: 0x0404
The ProcessInfo2
command queries the runtime for some basic information about the process. The returned payload has the same information as that of the ProcessInfo
command in addition to the managed entrypoint assembly name and CLR product version.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0404; 0x0000 }
There is no payload.
Header: { Magic; size; 0xFF00; 0x0000; }
Payload:
int64 processId
: the process id in the process's PID-spaceGUID runtimeCookie
: a 128-bit GUID that should be unique across PID-spacesstring commandLine
: the command line that invoked the process- Windows: will be the same as the output of
GetCommandLineW
- Non-Windows: will be the fully qualified path of the executable in
argv[0]
followed by all arguments as the appear inargv
separated by spaces, i.e.,/full/path/to/argv[0] argv[1] argv[2] ...
- Windows: will be the same as the output of
string OS
: the operating system that the process is running on- macOS =>
"macOS"
- Windows =>
"Windows"
- Linux =>
"Linux"
- other =>
"Unknown"
- macOS =>
string arch
: the architecture of the process- 32-bit =>
"x86"
- 64-bit =>
"x64"
- ARM32 =>
"arm32"
- ARM64 =>
"arm64"
- Other =>
"Unknown"
- 32-bit =>
string managedEntrypointAssemblyName
: the assembly name from the assembly identity of the entrypoint assembly of the process. This is the same value that is returned from executingSystem.Reflection.Assembly.GetEntryAssembly().GetName().Name
in the target process.string clrProductVersion
: the product version of the CLR of the process; may contain prerelease label information e.g.6.0.0-preview.6.#####
Returns:
struct Payload
{
uint64_t ProcessId;
LPCWSTR CommandLine;
LPCWSTR OS;
LPCWSTR Arch;
GUID RuntimeCookie;
LPCWSTR ManagedEntrypointAssemblyName;
LPCWSTR ClrProductVersion;
}
Available since .NET 7.0
Command Code: 0x0405
The EnablePerfMap
command instructs the runtime to start emitting perfmap or jitdump files for the process. These files are used by the perf tool to correlate jitted code addresses in a trace.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0405; 0x0000 }
Payload:
uint32_t perfMapType
: the type of generation to enable
Header: { Magic; 28; 0xFF00; 0x0000; }
EnablePerfMap
returns:
int32 hresult
: The result of enabling the perfmap or jitdump files (0
indicates success)
Inputs:
enum class PerfMapType
{
DISABLED = 0,
ALL = 1,
JITDUMP = 2,
PERFMAP = 3
}
struct Payload
{
uint32_t perfMapType;
}
Returns:
Payload
{
int32 hresult
}
Available since .NET 8.0
Command Code: 0x0406
The DisablePerfMap
command instructs the runtime to stop emitting perfmap or jitdump files for the process. These files are used by the perf tool to correlate jitted code addresses in a trace.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0405; 0x0000 }
Payload: There is no payload with this command.
Header: { Magic; 28; 0xFF00; 0x0000; }
DisablePerfMap
returns:
int32 hresult
: The result of enabling the perfmap or jitdump files (0
indicates success)
Returns:
Payload
{
int32 hresult
}
Available since .NET 8.0
Command Code: 0x0407
The ApplyStartupHook
command is used to provide a path to a managed assembly with a startup hook to the runtime. During diagnostic suspension, the startup hook path will be added list of hooks that the runtime will execute once it has been resumed.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0407; 0x0000 }
string startupHookPath
: The path to the managed assembly that contains the startup hook implementation.
Header: { Magic; size; 0xFF00; 0x0000; }
ApplyStartupHook
returns:
int32 hresult
: The result of adding the startup hook (0
indicates success)
Input:
Payload
{
string startupHookPath
}
Returns:
struct Payload
{
int32 hresult
}
Available since .NET 8.0
Command Code: 0x0408
The ProcessInfo3
command queries the runtime for some basic information about the process. The returned payload is versioned and fields will be added over time.
In the event of an error, the runtime will attempt to send an error message and subsequently close the connection.
Header: { Magic; Size; 0x0408; 0x0000 }
There is no payload.
Header: { Magic; size; 0xFF00; 0x0000; }
Payload:
uint32 version
: the version of the payload returned. Future versions can add new fields after the end of the current structure, but will never remove or change any field that has already been defined.uint64 processId
: the process id in the process's PID-spaceGUID runtimeCookie
: a 128-bit GUID that should be unique across PID-spacesstring commandLine
: the command line that invoked the process- Windows: will be the same as the output of
GetCommandLineW
- Non-Windows: will be the fully qualified path of the executable in
argv[0]
followed by all arguments as the appear inargv
separated by spaces, i.e.,/full/path/to/argv[0] argv[1] argv[2] ...
- Windows: will be the same as the output of
string OS
: the operating system that the process is running on- macOS =>
"macOS"
- Windows =>
"Windows"
- Linux =>
"Linux"
- other =>
"Unknown"
- macOS =>
string arch
: the architecture of the process- 32-bit =>
"x86"
- 64-bit =>
"x64"
- ARM32 =>
"arm32"
- ARM64 =>
"arm64"
- Other =>
"Unknown"
- 32-bit =>
string managedEntrypointAssemblyName
: the assembly name from the assembly identity of the entrypoint assembly of the process. This is the same value that is returned from executingSystem.Reflection.Assembly.GetEntryAssembly().GetName().Name
in the target process.string clrProductVersion
: the product version of the CLR of the process; may contain prerelease label information e.g.6.0.0-preview.6.#####
string runtimeIdentifier
: information to identify the platform this runtime targets, e.g.linux_musl_arm
64,linux_x64
, orwindows_x64
are all valid identifiers. See .NET RID Catalog for more information.
Returns:
struct Payload
{
uint32_t Version;
uint64_t ProcessId;
LPCWSTR CommandLine;
LPCWSTR OS;
LPCWSTR Arch;
GUID RuntimeCookie;
LPCWSTR ManagedEntrypointAssemblyName;
LPCWSTR ClrProductVersion;
LPCWSTR RuntimeIdentifier;
}
Available since .NET 8.0
In the event an error occurs in the handling of an Ipc Message, the Diagnostic Server will attempt to send an Ipc Message encoding the error and subsequently close the connection. The connection will be closed regardless of the success of sending the error message. The Client is expected to be resilient in the event of a connection being abruptly closed.
Errors are HRESULTS
encoded as int32_t
when sent back to the client. There are a few Diagnostics IPC specific HRESULT
s:
#define CORDIAGIPC_E_BAD_ENCODING = 0x80131384
#define CORDIAGIPC_E_UNKNOWN_COMMAND = 0x80131385
#define CORDIAGIPC_E_UNKNOWN_MAGIC = 0x80131386
#define CORDIAGIPC_E_UNKNOWN_ERROR = 0x80131387
Diagnostic Server errors are sent as a Diagnostic IPC Message with:
- a
command_set
of0xFF
- a
command_id
of0xFF
- a Payload consisting of a
int32_t
representing the error encountered (described above)
All errors will result in the Server closing the connection.
Error response Messages will be sent when:
- the client sends an improperly encoded Diagnostic IPC Message
- the client uses an unknown
command
- the client uses an unknown
magic
version string - the server encounters an unrecoverable error, e.g., OOM, transport error, runtime malfunction etc.
The client is expected to be resilient in the event that the Diagnostic Server fails to respond in a reasonable amount of time (this may be Command specific).
For example, if the Diagnostic Server finds incorrectly encoded data while parsing a Message, it would send the following Message in response:
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 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Header | Payload | ||||||||||||||||||||||||||
magic | size | command_set | command_id | reserved | Error Code | ||||||||||||||||||||||
"DOTNET_IPC_V1" | 28 | 0xFF | 0xFF | 0x0000 | 0x80131384 |
Available since .NET 5.0
A Diagnostic Port is a mechanism for communicating the Diagnostics IPC Protocol to a .NET application from out of process. There are two flavors of Diagnostic Port: connect
and listen
. A listen
Port is when the runtime creates an IPC transport and listens for incoming connections. The default Diagnostic Port is an example of a listen
Port. You cannot currently configure additional listen
Ports. A connect
Port is when the runtime attempts to connect to an IPC transport owned by another process. Upon connection to a connect
Port, the runtime will send an Advertise message signalling that it is ready to accept Diagnostics IPC Protocol commands. Each command consumes a connection, and the runtime will reconnect to the connect
Port to wait for more commands.
.NET applications can configure Diagnostic Ports with the following environment variables:
DOTNET_DiagnosticPorts=<port address>[,tag[...]][;<port address>[,tag[...]][...]]
where:
<port address>
is a NamedPipe name without\\.\pipe\
on Windows, and the full path to a Unix domain socket on other platformstag ::= <SUSPEND_MODE> | <PORT_TYPE>
<SUSPEND_MODE> ::= suspend | nosuspend
(default value is suspend)`<PORT_TYPE> ::= connect
(future types such as additional listen ports could be added to this list)
Example usage:
$ export DOTNET_DiagnosticPorts=$DOTNET_DiagnosticPorts;~/mydiagport.sock,nosuspend;
Any diagnostic ports specified in this configuration will be created in addition to the default port (dotnet-diagnostic-<pid>-<epoch>
). The suspend mode of the default port is set via the new environment variable DOTNET_DefaultDiagnosticPortSuspend
which defaults to 0
for nosuspend
.
Each port configuration specifies whether it is a suspend
or nosuspend
port. Ports specifying suspend
in their configuration will cause the runtime to pause early on in the startup path before most runtime subsystems have started. This allows any agent to receive a connection and properly setup before the application startup continues. Since multiple ports can individually request suspension, the resume
command needs to be sent by each suspended port connection before the runtime resumes execution.
If a config specifies multiple tag values from a tag type, for example "<path>,nosuspend,suspend,suspend,"
, only the first one is respected.
The port address value is required for a port configuration. If a configuration doesn't specify an address and only specifies tags, then the first tag will be treated as the path. For example, the configuration DOTNET_DiagnosticPorts=nosuspend,connect
would cause a port with the name nosuspend
to be created, in the default suspend
mode.
The runtime will make a best effort attempt to generate a port from a port configuration. A bad port configuration won't cause an error state, but could lead to consumed resources. For example it could cause the runtime to continuously poll for a connect port that will never exist.
When a Diagnostic Port is configured, the runtime will attempt to connect to the provided address in a retry loop while also listening on the traditional server. The retry loop has an initial timeout of 10ms with a falloff factor of 1.25x and a max timeout of 500 ms. A successful connection will result in an infinite timeout. The runtime is resilient to the remote end of the Diagnostic Port failing, e.g., closing, not Accepting
, etc.
Upon successful connection, the runtime will send a fixed-size, 34 byte buffer containing the following information:
char[8] magic
: (8 bytes)"ADVR_V1\0"
(ASCII chars + null byte)GUID runtimeCookie
: (16 bytes) CLR Instance Cookie (little-endian)uint64_t processId
: (8 bytes) PID (little-endian)uint16_t future
: (2 bytes) unused for future-proofing
With the following layout:
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 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
magic | runtimeCookie | processId | future | ||||||||||||||||||||||||||||||
"ADVR_V1\0" | 123e4567-e89b-12d3-a456-426614174000 | 12345 | 0x0000 |
This is a one-way transmission with no expectation of an ACK. The tool owning the Diagnostic Port is expected to consume this message and then hold on to the now active connection until it chooses to send a Diagnostics IPC command.
Due to the potential for an optional continuation in the Diagnostics IPC Protocol, each successful connection between the runtime and a Diagnostic Port is only usable once. As a result, a .NET process will attempt to reconnect to the diagnostic port immediately after every command that is sent across an active connection.
A typical dataflow has 2 actors, the Target application, T
and the Diagnostics Monitor Application, M
, and communicates like so:
T -> : Target attempts to connect to M, which may not exist yet
// M comes into existence
T -> M : [ Advertise ] - Target sends advertise message to Monitor
// 0 or more time passes
T <- M : [ Diagnostics IPC Protocol ] - Monitor sends a Diagnostics IPC Protocol command
T -> M : [ Advertise ] - Target reconnects to Monitor with a _new_ connection and re-sends the advertise message
It is important to emphasize that a connection should not be reused for multiple Diagnostic IPC Protocol commands.