Skip to content

Commit 58b88fa

Browse files
Bug fixes, updates and JSON output support
Changes: - updated nuget dependencies - handing of the RPC parser exception when communicating service is not available - added support for JSON output format (#19)
1 parent 4f03f32 commit 58b88fa

File tree

16 files changed

+158
-112
lines changed

16 files changed

+158
-112
lines changed

ms-dbgtools/amd64/dbghelp.dll

72.9 KB
Binary file not shown.

ms-dbgtools/amd64/symsrv.dll

36.9 KB
Binary file not shown.

ms-dbgtools/x86/dbghelp.dll

56.9 KB
Binary file not shown.

ms-dbgtools/x86/symsrv.dll

31.4 KB
Binary file not shown.

wtrace.cmd/Processing/ProcessTree.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ let printProcessTree state =
111111
let exitTime =
112112
if proc.ExitTime <> DateTime.MaxValue then sprintf "finished at %s" (proc.ExitTime.ToString("HH:mm:ss.ffff"))
113113
else ""
114-
printfn "%s├─ %s [%d] %s %s" ("" |> String.replicate depth) proc.ProcessName proc.SystemProcessId startTime exitTime
114+
eprintfn "%s├─ %s [%d] %s %s" ("" |> String.replicate depth) proc.ProcessName proc.SystemProcessId startTime exitTime
115115
node.Children.Values |> Seq.iter (printChildren (depth + 1))
116116

117117
for n in tree.Values do

wtrace.cmd/Processing/RpcResolver.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ module private H =
4949
|> Seq.collect (
5050
fun m ->
5151
logger.TraceVerbose($"[rpc] parsing module '%s{m}'")
52-
RpcServer.ParsePeFile(m, dbgHelpPath, symbolsPath, parserFlags))
52+
try
53+
RpcServer.ParsePeFile(m, dbgHelpPath, symbolsPath, parserFlags)
54+
with
55+
| ex ->
56+
logger.TraceErrorWithMessage($"[rpc] failed parsing module '%s{m}'", ex)
57+
[]
58+
)
5359
|> Seq.iter (resolveRpcServer state)
5460

5561
let resolveBindingAsync state binding (taskCount : int32 ref) =

wtrace.cmd/Program.fs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ let appAssembly = Assembly.GetEntryAssembly()
1717
let appName = appAssembly.GetName()
1818

1919
let showCopyright () =
20-
printfn ""
21-
printfn "%s v%s - collects process or system traces" appName.Name (appName.Version.ToString())
20+
eprintfn ""
21+
eprintfn "%s v%s - collects process or system traces" appName.Name (appName.Version.ToString())
2222
let customAttrs = appAssembly.GetCustomAttributes(typeof<AssemblyCompanyAttribute>, true);
2323
assert (customAttrs.Length > 0)
24-
printfn "Copyright (C) 2022 %s" (customAttrs.[0] :?> AssemblyCompanyAttribute).Company
25-
printfn "Visit https://wtrace.net to learn more"
26-
printfn ""
24+
eprintfn "Copyright (C) 2022 %s" (customAttrs.[0] :?> AssemblyCompanyAttribute).Company
25+
eprintfn "Visit https://wtrace.net to learn more"
26+
eprintfn ""
2727

2828
let showHelp () =
29-
printfn "Usage: %s [OPTIONS] [pid|imagename args]" appName.Name
30-
printfn @"
29+
eprintfn "Usage: %s [OPTIONS] [pid|imagename args]" appName.Name
30+
eprintfn @"
3131
Options:
3232
-f, --filter=FILTER Displays only events which satisfy a given FILTER.
3333
(Does not impact the summary)
@@ -39,6 +39,8 @@ Options:
3939
- shown in the summary.
4040
--symbols=SYMPATH Resolve stacks and RPC method names using the provided symbols path.
4141
--nosummary Prints only ETW events - no summary at the end.
42+
--output Format of the events in the output.
43+
Available options: 'freetext' (default) or 'json'
4244
-v, --verbose Shows wtrace diagnostics logs.
4345
-h, --help Shows this message and exits.
4446
@@ -96,9 +98,9 @@ let parseHandlers args =
9698
|> Set.add "process" // process handler is always on
9799
let handlers = handlerNames |> Set.toArray |> Array.map resolveHandler
98100

99-
printfn "HANDLERS"
100-
printfn " %s" (handlerNames |> String.concat ", ")
101-
printfn ""
101+
eprintfn "HANDLERS"
102+
eprintfn " %s" (handlerNames |> String.concat ", ")
103+
eprintfn ""
102104

103105
Ok handlers
104106
with
@@ -120,10 +122,10 @@ let parseFilters args =
120122
try
121123
let filters =
122124
filters |> List.map EventFilter.parseFilter
123-
printfn "FILTERS"
124-
if filters |> List.isEmpty then printfn " [none]"
125+
eprintfn "FILTERS"
126+
if filters |> List.isEmpty then eprintfn " [none]"
125127
else EventFilter.printFilters filters
126-
printfn ""
128+
eprintfn ""
127129
Ok (EventFilter.buildFilterFunction filters)
128130
with
129131
| EventFilter.ParseError msg -> Error msg
@@ -142,11 +144,11 @@ let checkElevated () =
142144
let finishProcessingAndShowSummary tstate counters (ct : CancellationToken) =
143145

144146
if RpcResolver.isRunning () then
145-
printf "\rResolving RPC endpoints (press Ctrl + C to stop) "
147+
eprintf "\rResolving RPC endpoints (press Ctrl + C to stop) "
146148
while not ct.IsCancellationRequested && RpcResolver.isRunning () do
147-
printf "."
149+
eprintf "."
148150
Async.Sleep(500) |> Async.RunSynchronously
149-
printfn ""
151+
eprintfn ""
150152

151153
TraceSummary.dump tstate counters
152154

@@ -160,7 +162,12 @@ let start (supportFilesDirectory : string) (args : Map<string, list<string>>) =
160162

161163
if [| "v"; "verbose" |] |> isFlagEnabled then
162164
Trace.AutoFlush <- true
163-
Logger.initialize(SourceLevels.Verbose, [ new TextWriterTraceListener(Console.Out) ])
165+
Logger.initialize(SourceLevels.Verbose, [ new TextWriterTraceListener(Console.Error) ])
166+
167+
let outputFormat =
168+
match args |> Map.tryFind "output" with
169+
| Some ["json"] -> TraceControl.OutputFormat.Json
170+
| _ -> TraceControl.OutputFormat.FreeText
164171

165172
let! filterEvents = parseFilters args
166173
let! handlers = parseHandlers args
@@ -174,17 +181,17 @@ let start (supportFilesDirectory : string) (args : Map<string, list<string>>) =
174181
| None ->
175182
match Environment.GetEnvironmentVariable("_NT_SYMBOL_PATH") with
176183
| v when v <> null ->
177-
printfn "Debug symbols path: %s" v
178-
printfn ""
184+
eprintfn "Debug symbols path: %s" v
185+
eprintfn ""
179186

180187
UseDbgHelp(dbgHelpPath, v)
181188
| _ -> DebugSymbolSettings.Ignore
182189
| Some paths ->
183190
Debug.Assert(paths.Length > 0)
184191
let symbolsPath = List.last paths
185192

186-
printfn "Debug symbols path: %s" symbolsPath
187-
printfn ""
193+
eprintfn "Debug symbols path: %s" symbolsPath
194+
eprintfn ""
188195

189196
UseDbgHelp(dbgHelpPath, symbolsPath)
190197

@@ -199,7 +206,7 @@ let start (supportFilesDirectory : string) (args : Map<string, list<string>>) =
199206
Console.CancelKeyPress.Add(
200207
fun ev ->
201208
if not tracingCts.IsCancellationRequested then
202-
printfn "Closing the trace session. Please wait..."
209+
eprintfn "Closing the trace session. Please wait..."
203210
ev.Cancel <- true
204211
tracingCts.Cancel()
205212
elif not processingCts.IsCancellationRequested then
@@ -213,27 +220,29 @@ let start (supportFilesDirectory : string) (args : Map<string, list<string>>) =
213220
match args |> Map.tryFind "" with
214221
| None when isSystemTrace args ->
215222
if not showSummary then
216-
printfn "WARNING: --nosummary does not take any effect in the system-only trace."
223+
eprintfn "WARNING: --nosummary does not take any effect in the system-only trace."
217224
TraceControl.traceSystemOnly cancellationTokens
218225

219226
| None ->
220-
TraceControl.traceEverything cancellationTokens handlers filterEvents showSummary debugSymbols
227+
TraceControl.traceEverything cancellationTokens handlers filterEvents showSummary debugSymbols outputFormat
221228

222229
| Some args ->
223230
let newConsole = ([| "newconsole" |] |> isFlagEnabled)
224231
let includeChildren = [| "c"; "children" |] |> isFlagEnabled
225232

226233
match args with
227234
| [ pid ] when isInteger pid ->
228-
TraceControl.traceRunningProcess cancellationTokens handlers filterEvents showSummary debugSymbols includeChildren (Int32.Parse(pid))
235+
TraceControl.traceRunningProcess cancellationTokens handlers filterEvents showSummary
236+
debugSymbols outputFormat includeChildren (Int32.Parse(pid))
229237
| args ->
230-
TraceControl.traceNewProcess cancellationTokens handlers filterEvents showSummary debugSymbols newConsole includeChildren args
238+
TraceControl.traceNewProcess cancellationTokens handlers filterEvents showSummary
239+
debugSymbols outputFormat newConsole includeChildren args
231240

232241
if not (TraceControl.sessionWaitEvent.WaitOne(TimeSpan.FromSeconds(3.0))) then
233-
printfn "WARNING: the session did not finish in the allotted time. Stop it manually: logman stop wtrace-rt -ets"
242+
eprintfn "WARNING: the session did not finish in the allotted time. Stop it manually: logman stop wtrace-rt -ets"
234243

235244
if TraceControl.lostEventsCount > 0 then
236-
printfn "WARNING: %d events were lost in the session. Check wtrace help at https://wtrace.net to learn more." TraceControl.lostEventsCount
245+
eprintfn "WARNING: %d events were lost in the session. Check wtrace help at https://wtrace.net to learn more." TraceControl.lostEventsCount
237246

238247
finishProcessingAndShowSummary traceState counters cancellationTokens.ProcessingCancellationToken
239248
}
@@ -250,5 +259,5 @@ let main (supportFilesDirectory : string) (argv : array<string>) =
250259
else
251260
match start supportFilesDirectory args with
252261
| Ok _ -> 0
253-
| Error msg -> printfn "ERROR: %s" msg; 1
262+
| Error msg -> eprintfn "ERROR: %s" msg; 1
254263

wtrace.cmd/TraceControl.fs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ open System.Diagnostics
66
open System.Reactive.Linq
77
open System.Reactive.Subjects
88
open System.Threading
9+
open System.Text.Json
910
open FSharp.Control.Reactive
1011
open LowLevelDesign.WTrace.Events
1112
open LowLevelDesign.WTrace.Tracing
@@ -22,6 +23,10 @@ type WorkCancellation = {
2223
ProcessingCancellationToken : CancellationToken
2324
}
2425

26+
type OutputFormat =
27+
| FreeText = 0
28+
| Json = 1
29+
2530
[<AutoOpen>]
2631
module private H =
2732
let rundownWaitEvent = new ManualResetEvent(false)
@@ -30,7 +35,7 @@ module private H =
3035
match s with
3136
| SessionRunning -> rundownWaitEvent.Set() |> ignore
3237
| SessionError msg ->
33-
printfn "ERROR: Error when starting the trace session\n%s" msg
38+
eprintfn "ERROR: Error when starting the trace session\n%s" msg
3439
rundownWaitEvent.Set() |> ignore
3540
sessionWaitEvent.Set() |> ignore
3641
| SessionStopped n ->
@@ -53,11 +58,15 @@ module private H =
5358
let etwsub = etwObservable.Connect()
5459
let reg = ct.Register(fun () -> etwsub.Dispose())
5560

56-
printfn "Starting the tracing session (might take a moment). Press Ctrl + C to exit."
61+
eprintfn "Starting the tracing session (might take a moment). Press Ctrl + C to exit."
5762
rundownWaitEvent.WaitOne() |> ignore
5863

5964
Disposable.compose etwsub reg
6065

66+
let onEventJson (TraceEventWithFields (ev, _)) =
67+
printfn "%s" (JsonSerializer.Serialize(ev))
68+
Interlocked.Exchange(&lastEventTime, DateTime.Now.Ticks) |> ignore
69+
6170
let onEvent (TraceEventWithFields (ev, _)) =
6271
let getPath v = if v = "" then "" else sprintf " '%s'" v
6372
let getDesc v = if v = "" then "" else sprintf " %s" v
@@ -68,7 +77,7 @@ module private H =
6877
Interlocked.Exchange(&lastEventTime, DateTime.Now.Ticks) |> ignore
6978

7079
let onError (ex : Exception) =
71-
printfn "ERROR: an error occured while collecting the trace - %s" (ex.ToString())
80+
eprintfn "ERROR: an error occured while collecting the trace - %s" (ex.ToString())
7281

7382
let traceSystemOnly ct =
7483
result {
@@ -91,7 +100,7 @@ let traceSystemOnly ct =
91100
return (tstate, counters)
92101
}
93102

94-
let traceEverything ct handlers filter showSummary debugSymbols =
103+
let traceEverything ct handlers filter showSummary debugSymbols outputFormat =
95104
result {
96105
let settings = {
97106
Handlers = handlers
@@ -106,6 +115,8 @@ let traceEverything ct handlers filter showSummary debugSymbols =
106115
etwObservable
107116
|> Observable.filter (TraceEventProcessor.processAndFilterEvent tstate)
108117

118+
let onEvent = if outputFormat = OutputFormat.Json then onEventJson else onEvent
119+
109120
eventObservable
110121
|> Observable.filter (fun (TraceEventWithFields (ev, _)) -> filter ev)
111122
|> Observable.subscribeWithCallbacks onEvent onError ignore
@@ -139,13 +150,15 @@ module private ProcessApi =
139150
else
140151
waitForProcessExit ct hProcess
141152

142-
let traceProcess ct handlers filter showSummary debugSymbols includeChildren (pid, hProcess, hThread) =
153+
let traceProcess ct handlers filter showSummary debugSymbols outputFormat includeChildren (pid, hProcess, hThread) =
143154
result {
144155
let settings = {
145156
Handlers = handlers
146157
EnableStacks = false
147158
}
148159

160+
let onEvent = if outputFormat = OutputFormat.Json then onEventJson else onEvent
161+
149162
let etwObservable = createEtwObservable settings
150163

151164
let processFilter = ProcessFilter.Process (pid, includeChildren)
@@ -173,7 +186,7 @@ module private ProcessApi =
173186

174187
let! processFinished = waitForProcessExit ct.TracingCancellationToken hProcess
175188
if processFinished then
176-
printfn "Process (%d) exited." pid
189+
eprintfn "Process (%d) exited." pid
177190

178191
let mutable savedLastEventTime = Interlocked.Read(&lastEventTime)
179192
let rec waitForMoreEvents () =
@@ -195,19 +208,19 @@ module private ProcessApi =
195208
}
196209

197210

198-
let traceNewProcess ct handlers filter showSummary debugSymbols newConsole includeChildren (args : list<string>) =
211+
let traceNewProcess ct handlers filter showSummary debugSymbols outputFormat newConsole includeChildren (args : list<string>) =
199212
result {
200213
Debug.Assert(args.Length > 0, "[TraceControl] invalid number of arguments")
201214
let! processIds = WinApi.startProcessSuspended args newConsole
202215

203-
return! ProcessApi.traceProcess ct handlers filter showSummary debugSymbols includeChildren processIds
216+
return! ProcessApi.traceProcess ct handlers filter showSummary debugSymbols outputFormat includeChildren processIds
204217
}
205218

206-
let traceRunningProcess ct handlers filter showSummary debugSymbols includeChildren pid =
219+
let traceRunningProcess ct handlers filter showSummary debugSymbols outputFormat includeChildren pid =
207220
result {
208221
let! hProcess = WinApi.openRunningProcess pid
209222
let processIds = (pid, hProcess, HANDLE.INVALID_HANDLE_VALUE)
210223

211-
return! ProcessApi.traceProcess ct handlers filter showSummary debugSymbols includeChildren processIds
224+
return! ProcessApi.traceProcess ct handlers filter showSummary debugSymbols outputFormat includeChildren processIds
212225
}
213226

wtrace.cmd/TraceSummary.fs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ module private H =
1010
let printTitle (title : string) =
1111
let separator = "--------------------------------"
1212
let space = Math.Max(0, (separator.Length - title.Length) / 2)
13-
printfn ""
14-
printfn "%s" separator
15-
printfn "%s%s" (" " |> String.replicate space) title
16-
printfn "%s" separator
13+
eprintfn ""
14+
eprintfn "%s" separator
15+
eprintfn "%s%s" (" " |> String.replicate space) title
16+
eprintfn "%s" separator
1717

1818
let getCounterValue (counter : TraceCounters.NumericCounter) key =
1919
match counter.TryGetValue(key) with
@@ -31,7 +31,7 @@ module private H =
3131
(p, received + sent, sent, received))
3232
|> Seq.sortByDescending (fun (_, total, _, _) -> total)
3333
|> Seq.iter (fun (path, total, sent, received) ->
34-
printfn "%s Total: %dB, Sent: %dB, Received: %dB" path total sent received)
34+
eprintfn "%s Total: %dB, Sent: %dB, Received: %dB" path total sent received)
3535

3636

3737
let dump traceState (counters : TraceCounters.Counters) =
@@ -50,7 +50,7 @@ let dump traceState (counters : TraceCounters.Counters) =
5050
(p, read + written, written, read))
5151
|> Seq.sortByDescending (fun (_, total, _, _) -> total)
5252
|> Seq.iter (fun (path, total, written, read) ->
53-
printfn "'%s' Total: %dB, Writes: %dB, Reads: %dB" path total written read)
53+
eprintfn "'%s' Total: %dB, Writes: %dB, Reads: %dB" path total written read)
5454

5555
dumpNetworkStatistics "TCP/IP" counters.TcpReceivedBytes counters.TcpSentBytes
5656
dumpNetworkStatistics "UDP" counters.UdpReceivedBytes counters.UdpSentBytes
@@ -66,7 +66,7 @@ let dump traceState (counters : TraceCounters.Counters) =
6666
| (true, procedures) when procedures.Length > procNum ->
6767
sprintf "%O (%s) [%d]{%s}" interfaceUuid binding procNum procedures[procNum]
6868
| _ -> sprintf "%O (%s) [%d]" interfaceUuid binding procNum
69-
printfn "%s calls: %d" path total
69+
eprintfn "%s calls: %d" path total
7070
)
7171

7272
if counters.RpcClientCalls.Count > 0 then
@@ -85,7 +85,7 @@ let dump traceState (counters : TraceCounters.Counters) =
8585
|> Seq.iter (fun (baseAddr, (count, time)) ->
8686
let img = traceState.LoadedSystemImages[baseAddr]
8787
let time = time.ToString("#,0.000")
88-
printfn "'%s', Total: %s ms (%d event(s))" img.FileName time count)
88+
eprintfn "'%s', Total: %s ms (%d event(s))" img.FileName time count)
8989

9090
if counters.IsrCalls.Count > 0 then
9191
printTitle "ISR"
@@ -95,5 +95,5 @@ let dump traceState (counters : TraceCounters.Counters) =
9595
|> Seq.iter (fun (baseAddr, (count, time)) ->
9696
let img = traceState.LoadedSystemImages[baseAddr]
9797
let time = time.ToString("#,0.000")
98-
printfn "'%s', Total: %s ms (%d event(s))" img.FileName time count)
98+
eprintfn "'%s', Total: %s ms (%d event(s))" img.FileName time count)
9999
)

wtrace.cmd/Tracing/EventFilter.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ module EventFilter =
126126
| Details (op, s) -> ("Details", sprintf "%s '%s'" op s)
127127

128128
let printFiltersGroup name defs =
129-
printfn " %s" name
130-
printfn " %s" (defs |> String.concat " OR ")
129+
eprintfn " %s" name
130+
eprintfn " %s" (defs |> String.concat " OR ")
131131

132132
filters
133133
|> Seq.map buildFilterDescription

0 commit comments

Comments
 (0)