added new command to dump all active incoming http requests (#4536)
authorSainath Reddy G N V <144475740+sainath-reddy-gnv@users.noreply.github.com>
Mon, 4 Mar 2024 20:55:19 +0000 (15:55 -0500)
committerGitHub <noreply@github.com>
Mon, 4 Mar 2024 20:55:19 +0000 (12:55 -0800)
Hello everyone, I created a simple extension to dump out all active
incoming http requests.

I'm also planning to add time spent column but need help in getting the
dump time stamp. Any idea which api to use?

![image](https://github.com/dotnet/diagnostics/assets/144475740/df9cac92-20c4-4afd-a782-2a824914a5f2)

---------

Co-authored-by: Sainath Reddy G N V <nagosang@microsoft.com>
src/Microsoft.Diagnostics.ExtensionCommands/DumpHttpRequestsCommand.cs [new file with mode: 0644]
src/SOS/Strike/managedcommands.cpp
src/SOS/Strike/sos.def
src/SOS/lldbplugin/soscommand.cpp

diff --git a/src/Microsoft.Diagnostics.ExtensionCommands/DumpHttpRequestsCommand.cs b/src/Microsoft.Diagnostics.ExtensionCommands/DumpHttpRequestsCommand.cs
new file mode 100644 (file)
index 0000000..9eb932c
--- /dev/null
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.ExtensionCommands.Output;
+using Microsoft.Diagnostics.Runtime;
+using static Microsoft.Diagnostics.ExtensionCommands.Output.ColumnKind;
+
+namespace Microsoft.Diagnostics.ExtensionCommands
+{
+    [Command(Name = "dumphttprequests", Aliases = new string[] { "DumpHttpRequests" }, Help = "Shows all currently active incoming HTTP requests.")]
+    public class DumpHttpRequestsCommand : ClrRuntimeCommandBase
+    {
+        public override void Invoke()
+        {
+            List<(ulong Address, string Method, string Protocol, string Url)> requests = new();
+            if (Runtime.Heap.CanWalkHeap)
+            {
+                foreach (ClrObject obj in Runtime.Heap.EnumerateObjects())
+                {
+                    Console.CancellationToken.ThrowIfCancellationRequested();
+
+                    if (!obj.IsValid || obj.IsNull)
+                    {
+                        continue;
+                    }
+
+                    if (obj.Type?.Name?.Equals("Microsoft.AspNetCore.Http.DefaultHttpContext") ?? false)
+                    {
+                        ClrObject collection = obj.ReadValueTypeField("_features").ReadObjectField("<Collection>k__BackingField");
+                        if (!collection.IsNull)
+                        {
+                            string method = collection.ReadStringField("<Method>k__BackingField") ?? "";
+                            string scheme = collection.ReadStringField("<Scheme>k__BackingField") ?? "";
+                            string path = collection.ReadStringField("<Path>k__BackingField") ?? "";
+                            string query = collection.ReadStringField("<QueryString>k__BackingField") ?? "";
+                            requests.Add((obj.Address, method, $"{scheme}", $"{path}{query}"));
+                        }
+                    }
+                }
+            }
+            else
+            {
+                Console.WriteLine("The GC heap is not in a valid state for traversal.");
+            }
+
+            if (requests.Count > 0)
+            {
+                PrintRequests(requests);
+                Console.WriteLine($"Found {requests.Count} active requests");
+            }
+            else
+            {
+                Console.WriteLine("No requests found");
+            }
+        }
+
+        public void PrintRequests(List<(ulong Address, string Method, string scheme, string Url)> requests)
+        {
+            Column methodColumn = Text.GetAppropriateWidth(requests.Select(r => r.Method)).WithAlignment(Align.Left);
+            Column schemeColumn = Text.GetAppropriateWidth(requests.Select(r => r.scheme)).WithAlignment(Align.Left);
+            Column urlColumn = Text.GetAppropriateWidth(requests.Select(r => r.Url)).WithAlignment(Align.Left);
+            Table output = new(Console, DumpObj.WithAlignment(Align.Left), methodColumn, schemeColumn, urlColumn); ;
+            output.WriteHeader("Address", "Method", "Scheme", "Url");
+
+            foreach ((ulong address, string method, string scheme, string url) in requests)
+            {
+                Console.CancellationToken.ThrowIfCancellationRequested();
+                output.WriteRow(address, method, scheme, url);
+            }
+        }
+    }
+}
index 2a7b33615bc8d58f8e79c0d122c0fc51d4552c09..a255795cd9a4fbca3f7b089b980b134ee21306ef 100644 (file)
@@ -180,6 +180,12 @@ DECLARE_API(sizestats)
     return ExecuteManagedOnlyCommand("sizestats", args);
 }
 
+DECLARE_API(DumpHttpRequests)
+{
+    INIT_API_EXT();
+    return ExecuteManagedOnlyCommand("dumphttprequests", args);
+}
+
 typedef HRESULT (*PFN_COMMAND)(PDEBUG_CLIENT client, PCSTR args);
 
 //
index 7f594e187a21c2735b6f17bd4efcd405428dc542..05c873d3ea2a4ce928366b8a4b980b8d39d94c13 100644 (file)
@@ -29,6 +29,8 @@ EXPORTS
     DumpDomain
     dumpdomain=DumpDomain
     dumpexceptions
+    DumpHttpRequests
+    dumphttprequests=DumpHttpRequests
 #ifdef TRACE_GC
     DumpGCLog
     dumpgclog=DumpGCLog
index 95847b9bce4b43f94f325575d9f2a18a1a0b9b4c..7edf9fca72fdb6300b161239b27f4bf3c30865c8 100644 (file)
@@ -173,6 +173,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     g_services->AddCommand("dumpdomain", new sosCommand("DumpDomain"), "Displays information about the all assemblies within all the AppDomains or the specified one.");
     g_services->AddCommand("dumpgcdata", new sosCommand("DumpGCData"), "Displays information about the GC data.");
     g_services->AddManagedCommand("dumpheap", "Displays info about the garbage-collected heap and collection statistics about objects.");
+    g_services->AddManagedCommand("dumphttprequests", "Shows all currently active incoming HTTP requests.");
     g_services->AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that's associated with a managed method.");
     g_services->AddCommand("dumplog", new sosCommand("DumpLog"), "Writes the contents of an in-memory stress log to the specified file.");
     g_services->AddCommand("dumpmd", new sosCommand("DumpMD"), "Displays information about a MethodDesc structure at the specified address.");