From dcf49f703a56360f3a51c526fbd2888e25832061 Mon Sep 17 00:00:00 2001 From: Sebastian Solnica Date: Tue, 2 Mar 2021 19:50:38 +0100 Subject: [PATCH] Added support for UDP (#14) --- wtrace.cmd/Events/UdpIp.fs | 65 ++++++++++++++++++++++++++++++ wtrace.cmd/Program.fs | 4 +- wtrace.cmd/TraceStatistics.fs | 76 +++++++++++++++++++++-------------- wtrace.cmd/wtrace.cmd.fsproj | 1 + wtrace/wtrace.csproj | 4 +- 5 files changed, 117 insertions(+), 33 deletions(-) create mode 100644 wtrace.cmd/Events/UdpIp.fs diff --git a/wtrace.cmd/Events/UdpIp.fs b/wtrace.cmd/Events/UdpIp.fs new file mode 100644 index 0000000..a7a06ae --- /dev/null +++ b/wtrace.cmd/Events/UdpIp.fs @@ -0,0 +1,65 @@ +module LowLevelDesign.WTrace.Events.UdpIp + +open System +open Microsoft.Diagnostics.Tracing +open Microsoft.Diagnostics.Tracing.Parsers.Kernel +open LowLevelDesign.WTrace +open LowLevelDesign.WTrace.Events +open LowLevelDesign.WTrace.Events.HandlerCommons + +type private UdpIpHandlerState = { + Broadcast : EventBroadcast +} + +[] +module private H = + + let noFields = Array.empty + + let handleUdpIpFail id state (ev : UdpIpFailTraceData) = + let ev = toEvent ev id "" "" "" ev.FailureCode + state.Broadcast.publishTraceEvent (TraceEventWithFields (ev, noFields)) + + let handleUdpIpData id state (ev : UdpIpTraceData) = + let fields = [| + struct (nameof ev.size, FI32 ev.size) |] + + let details = sprintf "size: %d" ev.size + let path = sprintf "%s:%d -> %s:%d" (ev.saddr.ToString()) ev.sport (ev.daddr.ToString()) ev.dport + let ev = toEvent ev id "" path details WinApi.eventStatusUndefined + state.Broadcast.publishTraceEvent (TraceEventWithFields (ev, fields |> Array.map (toEventField id))) + + let handleUdpIp6Data id state (ev : UpdIpV6TraceData) = + let fields = [| + struct (nameof ev.size, FI32 ev.size) |] + + let details = sprintf "size: %d" ev.size + let path = sprintf "%s:%d -> %s:%d" (ev.saddr.ToString()) ev.sport (ev.daddr.ToString()) ev.dport + let activityId = sprintf "conn#%d" ev.connid + let ev = toEvent ev id activityId path details WinApi.eventStatusUndefined + state.Broadcast.publishTraceEvent (TraceEventWithFields (ev, fields |> Array.map (toEventField id))) + + let subscribe (source : TraceEventSource, isRundown, idgen, state : obj) = + let state = state :?> UdpIpHandlerState + let handleEvent h = Action<_>(handleEvent idgen state h) + if not isRundown then + source.Kernel.add_UdpIpFail(handleEvent handleUdpIpFail) + source.Kernel.add_UdpIpRecv(handleEvent handleUdpIpData) + source.Kernel.add_UdpIpRecvIPV6(handleEvent handleUdpIp6Data) + source.Kernel.add_UdpIpSend(handleEvent handleUdpIpData) + source.Kernel.add_UdpIpSendIPV6(handleEvent handleUdpIp6Data) + + +let createEtwHandler () = + { + KernelFlags = NtKeywords.NetworkTCPIP + KernelStackFlags = NtKeywords.NetworkTCPIP + KernelRundownFlags = NtKeywords.None + Providers = Array.empty + Initialize = + fun (broadcast) -> ({ + Broadcast = broadcast + } :> obj) + Subscribe = subscribe + } + diff --git a/wtrace.cmd/Program.fs b/wtrace.cmd/Program.fs index 25f8dc6..d04da7b 100644 --- a/wtrace.cmd/Program.fs +++ b/wtrace.cmd/Program.fs @@ -47,6 +47,7 @@ Options: registry - to receive Registry events (voluminous, disabled by default) rpc - to receive RPC events tcp - to receive TCP/IP events + udp - to receive UDP events Example: --handlers 'tcp,file,registry' @@ -79,6 +80,7 @@ let parseHandlers args = elif name >=< "registry" then Registry.createEtwHandler () elif name >=< "rpc" then Rpc.createEtwHandler () elif name >=< "tcp" then TcpIp.createEtwHandler () + elif name >=< "udp" then UdpIp.createEtwHandler () else failwith (sprintf "Invalid handler name: '%s'" name) try @@ -96,7 +98,7 @@ let parseHandlers args = | Failure msg -> Error msg match args |> Map.tryFind "handlers" with - | None -> createHandlers "process,file,rpc,tcp" + | None -> createHandlers "process,file,rpc,tcp,udp" | Some [ handler ] -> if isSystemTrace args then Error ("Handlers are not allowed in the system trace.") diff --git a/wtrace.cmd/TraceStatistics.fs b/wtrace.cmd/TraceStatistics.fs index 61d6947..804be0f 100644 --- a/wtrace.cmd/TraceStatistics.fs +++ b/wtrace.cmd/TraceStatistics.fs @@ -140,19 +140,7 @@ module TraceStatistics = [] module private H = - type ImageInMemory = { - BaseAddress : uint64 - ImageSize : int32 - FileName : string - } - - let fileReadBytes = Dictionary() - let fileWrittenBytes = Dictionary() - let networkReceivedBytes = Dictionary() - let networkSentBytes = Dictionary() - let rpcCalls = Dictionary() - let dpcCalls = Dictionary() - let isrCalls = Dictionary() + type NumericCounter = Dictionary let updateCounter (counter : Dictionary) key count = match counter.TryGetValue(key) with @@ -179,6 +167,35 @@ module TraceStatistics = | (true, n) -> n | _ -> 0UL + let dumpNetworkStatistics title (networkReceivedBytes : NumericCounter) (networkSentBytes : NumericCounter) = + if networkReceivedBytes.Count > 0 || networkSentBytes.Count > 0 then + printTitle title + networkSentBytes.Keys + |> Seq.append networkReceivedBytes.Keys + |> Seq.distinct + |> Seq.map(fun p -> + let (sent, received) = (getCounterValue networkSentBytes p, getCounterValue networkReceivedBytes p) + (p, received + sent, sent, received)) + |> Seq.sortByDescending (fun (_, total, _, _) -> total) + |> Seq.iter (fun (path, total, sent, received) -> + printfn "%s Total: %dB, Sent: %dB, Received: %dB" path total sent received) + + type ImageInMemory = { + BaseAddress : uint64 + ImageSize : int32 + FileName : string + } + + let fileReadBytes = NumericCounter() + let fileWrittenBytes = NumericCounter() + let tcpReceivedBytes = NumericCounter() + let tcpSentBytes = NumericCounter() + let udpReceivedBytes = NumericCounter() + let udpSentBytes = NumericCounter() + let rpcCalls = NumericCounter() + let dpcCalls = Dictionary() + let isrCalls = Dictionary() + module private SystemImages = let baseAddresses = List(200) @@ -233,17 +250,25 @@ module TraceStatistics = elif ev.EventName === "FileIO/Write" then updateCounter fileWrittenBytes ev.Path (getUI64FieldValue fields "ExtraInfo") elif ev.EventName === "TcpIp/Recv" then - updateCounter networkReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) + updateCounter tcpReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) elif ev.EventName === "TcpIp/Send" then - updateCounter networkSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) + updateCounter tcpSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) elif ev.EventName === "TcpIp/RecvIPv6" then - updateCounter networkReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) + updateCounter tcpReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) elif ev.EventName === "TcpIp/SendIPv6" then - updateCounter networkSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) + updateCounter tcpSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) + elif ev.EventName === "UdpIp/Recv" then + updateCounter udpReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) + elif ev.EventName === "UdpIp/Send" then + updateCounter udpSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) + elif ev.EventName === "UdpIp/RecvIPv6" then + updateCounter udpReceivedBytes ev.Path (uint64 (getI32FieldValue fields "size")) + elif ev.EventName === "UdpIp/SendIPv6" then + updateCounter udpSentBytes ev.Path (uint64 (getI32FieldValue fields "size")) elif ev.EventName.StartsWith("RPC/", StringComparison.Ordinal) then match rpcCalls.TryGetValue(ev.Path) with - | (true, n) -> rpcCalls.[ev.Path] <- n + 1 - | (false, _) -> rpcCalls.Add(ev.Path, 1) + | (true, n) -> rpcCalls.[ev.Path] <- n + 1UL + | (false, _) -> rpcCalls.Add(ev.Path, 1UL) elif ev.EventName === "SystemImage/Load" then let image = { BaseAddress = getUI64FieldValue fields "ImageBase" ImageSize = getI32FieldValue fields "ImageSize" @@ -279,17 +304,8 @@ module TraceStatistics = |> Seq.iter (fun (path, total, written, read) -> printfn "'%s' Total: %dB, Writes: %dB, Reads: %dB" path total written read) - if networkReceivedBytes.Count > 0 || networkSentBytes.Count > 0 then - printTitle "TCP/IP" - networkSentBytes.Keys - |> Seq.append networkReceivedBytes.Keys - |> Seq.distinct - |> Seq.map(fun p -> - let (sent, received) = (getCounterValue networkSentBytes p, getCounterValue networkReceivedBytes p) - (p, received + sent, sent, received)) - |> Seq.sortByDescending (fun (_, total, _, _) -> total) - |> Seq.iter (fun (path, total, sent, received) -> - printfn "%s Total: %dB, Sent: %dB, Received: %dB" path total sent received) + dumpNetworkStatistics "TCP/IP" tcpReceivedBytes tcpSentBytes + dumpNetworkStatistics "UDP" udpReceivedBytes udpSentBytes if rpcCalls.Count > 0 then printTitle "RPC" diff --git a/wtrace.cmd/wtrace.cmd.fsproj b/wtrace.cmd/wtrace.cmd.fsproj index 427857c..43c3fad 100644 --- a/wtrace.cmd/wtrace.cmd.fsproj +++ b/wtrace.cmd/wtrace.cmd.fsproj @@ -21,6 +21,7 @@ + diff --git a/wtrace/wtrace.csproj b/wtrace/wtrace.csproj index e69c585..fee1d55 100644 --- a/wtrace/wtrace.csproj +++ b/wtrace/wtrace.csproj @@ -6,8 +6,8 @@ LowLevelDesign.WTrace Sebastian Solnica Sebastian Solnica (lowleveldesign.org) - 3.0.0.0 - 3.0.0.0 + 3.1.0.0 + 3.1.0.0 en ..\bin\wtrace true