Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add DiagnosticsAgent API - #931

Closed
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec
Closed

Add DiagnosticsAgent API#931
josalem wants to merge 10 commits into
dotnet:masterfrom
josalem:dev/josalem/diagnostics-agent-api-spec

Conversation

@josalem

@josalemjosalem commented Mar 19, 2020

Copy link
Copy Markdown
Contributor

The DiagnosticsAgent API can be used to create a Diagnostics IPC Protocol server. A working sample is provided in the updated documentation. New connections trigger an event that provides users with a DiagnosticsClient object that can be used the same as the current implementation.

N.B.: the implementation of this API will leverage dotnet/runtime#33307. I have done local testing with a version of this API and things worked well. Check out the dotnet-reverse tool in my hack branch. Note that the other tools will be broken in that branch and only dotnet-reverse builds.

CC - @tommcdon@davidfowl

@josalemjosalem added this to the 5.0 milestone Mar 19, 2020
@josalem
josalem requested review from noahfalk and sywhangMarch 19, 2020 23:06
@josalemjosalem self-assigned this Mar 19, 2020
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
Comment threaddocumentation/design-docs/diagnostics-client-library.md Outdated
@josalem

Copy link
Copy Markdown
ContributorAuthor

@sywhang and I chatted offline about whether the OnDiagnosticsConnection event needs to be public. He suggested an alternative where it is private and the handler is passed in via the ctor, e.g.,

public DiagnosticsAgent(string ipcEndpointAddress, Action<DiagnosticsConnectionEventArgs> connectionHandler);
Action<DiagnosticsConnectionEventArgs>connectionHandler=(eventArgs)=>{clientDict[eventArgs.RuntimeInstanceCookie]=(eventArgs.ProcessId,eventArgs.Client);Console.Write($"== New Connection: instanceCookie: {eventArgs.RuntimeInstanceCookie}, ProcessId: {eventArgs.ProcessId}\n> ");};using(DiagnosticsAgentagent=newDiagnosticsAgent(address,connectionHandler)){// ...}

@josalem

Copy link
Copy Markdown
ContributorAuthor

CC @wiktork

@sywhang

Copy link
Copy Markdown
Contributor

My suggestion was mostly based on whether the user would need to ever change it after creating the DiagnosticsAgent. It wasn't clear to me whether there would be a use case involving that, so I think it would be safer to first design it in a way that explicitly bans user from changing it.

If it is made public property, it is possible for a user to create a server socket and forget to create a handler, and never handle anything from the incoming connections. Making it an argument to the constructor makes it more clear for the user to create that handler with the server socket, which should be what they are doing with the public constructor regardless.

@tommcdontommcdon added the Priority:1 Work that is critical for the release, but we could probably ship without label Mar 20, 2020
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>
{
clientDict[eventArgs.RuntimeInstanceCookie] = (eventArgs.ProcessId, eventArgs.Client);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there be some mechanism by which multiple DiagnosticsClients can be established to the same process from one DiagnosticsAgent? I thinking that dotnet-monitory might need this if, for example, there is a active session for capturing logs, metrics, etc and then there is another request to capture dumps. I don't know if dotnet-monitor would be able to use the same connection concurrently for different collections of data.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I don't think a connection should include the DiagnosticClient. I'd suggest the callback arg/return value can be used to create an arbitrary number of DiagnosticClients. For example:

var agent = new DiagnosticsAgent(address);
var runtimeEndpoint = await agent.ListenForRuntimesAsync();
Console.WriteLine("Dotnet process detected with pid {runtimeEndpoint.Pid}");
// only listening to one for simplicity
while(true)
{
DotnetMonitorRequest r = await webserver.GetNextRequestAsync();
DiagnosticClient c = new DiagnosticClient(runtimeEndpoint);
Task.Run(() => HandleRequest(r,c));
}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation. You should already be able to start N<64 tracing sessions per process at once, and use the same DiagnosticsClient to capture dumps, gcdumps, etc. at the same time.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs? Right now, DiagnosticClients abstract the connection process entirely for both traditional and reversed connections. Every time you issue a command for a traditional connection, it reconnects to the underlying transport. This new iteration, effectively does the same thing for reverse connections, but does so by caching the reverse connection each time the runtime advertises.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's anything preventing you from using the same DiagnosticsClient for multiple actions at once, even in the current implementation

You could design it to work, but by convention .NET types are not considered thread-safe unless specifically documented otherwise. We could implement and document that DiagnosticsClient is thread-safe, but people will probably assume that it isn't by default. I'd recommend going with convention is easier than trying to explain that the API supports free-threaded use.

Is the suggestion that DiagnosticClients should be disposable, e.g., use once, constructs?

I think we should allow, but not require, that kind of usage.

{
using (DiagnosticsAgent agent = new DiagnosticsAgent(address))
{
agent.OnDiagnosticsConnection += (sender, eventArgs) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this more of a listener/socket pull style API?

varagent=newDiagnosticsAgent(address);while(true){varconnection=awaitagent.AcceptAsync();_=ProcessConnectionAsync(connection);}

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would DiagnosticsAgent.AcceptAsync() return a connectionevery time a runtime instance connects to the agent or only the first time? The current model raises the OnDiagnosticsConnection event on only the first connection and users are expected to reuse the DiagnosticsClient (which abstracts subsequent reconnects) they get just like they use the traditional DiagnosticsClient today. if we change it to be "every time an instance connects" we may want to modify the DiagnosticsClient API for dispatching commands, or not use it entirely.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest it is first time only. I am hoping to preserve the model that you get a DiagnosticClient and then you send an arbitrary number of commands without making the user aware of the underlying transport stream management.

@josalem

Copy link
Copy Markdown
ContributorAuthor

This PR is no longer needed since #1303 went in.

@josalemjosalem closed this Aug 16, 2020
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jan 17, 2024
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentationDocumentation related issueMicrosoft.Diagnostics.NETCore.ClientPriority:1Work that is critical for the release, but we could probably ship without

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@josalem@sywhang@davidfowl@noahfalk@jander-msft@tommcdon