|
3 | 3 | # |
4 | 4 | # Various miscellaneous utility functions reside here. |
5 | 5 | import osproc, pegs, strutils, os, uri, sets, json, parseutils, strformat, |
6 | | - sequtils, macros, times, terminal |
7 | | -when defined(instrument): |
8 | | - import std/genasts |
| 6 | + sequtils, macros, times |
9 | 7 |
|
10 | 8 | from net import SslCVerifyMode, newContext, SslContext |
11 | 9 |
|
@@ -225,22 +223,107 @@ proc newSSLContext*(disabled: bool): SslContext = |
225 | 223 | sslVerifyMode = CVerifyNone |
226 | 224 | return newContext(verifyMode = sslVerifyMode) |
227 | 225 |
|
228 | | -template measureTime*(name: static string, traceStack: bool, body: untyped) = |
| 226 | +when defined(instrument): |
| 227 | + import std/[tables, terminal, genasts] |
| 228 | + |
| 229 | + type |
| 230 | + CallRecord = object |
| 231 | + name: string |
| 232 | + totalTime: Duration |
| 233 | + callCount: int |
| 234 | + children: seq[int] # Indices of child records |
| 235 | + parent: int # Index of parent record (-1 for root) |
| 236 | + |
| 237 | + var gCallRecords: seq[CallRecord] |
| 238 | + var gCallStack: seq[int] # Stack of record indices |
| 239 | + var gNameToIndex: Table[string, int] # Map name+parent to record index |
| 240 | + var gInstrumentDepth: int |
| 241 | + |
| 242 | +proc getOrCreateRecord(name: string, parentIdx: int): int {.inline.} = |
| 243 | + ## Gets or creates a call record for the given function and parent |
229 | 244 | when defined(instrument): |
230 | | - let starts = times.now() |
231 | | - body |
232 | | - let ends = (times.now() - starts) |
233 | | - let dur = ends.inSeconds |
| 245 | + let key = name & ":" & $parentIdx |
| 246 | + if key in gNameToIndex: |
| 247 | + return gNameToIndex[key] |
| 248 | + |
| 249 | + result = gCallRecords.len |
| 250 | + gCallRecords.add(CallRecord( |
| 251 | + name: name, |
| 252 | + totalTime: initDuration(), |
| 253 | + callCount: 0, |
| 254 | + children: @[], |
| 255 | + parent: parentIdx |
| 256 | + )) |
| 257 | + gNameToIndex[key] = result |
| 258 | + |
| 259 | + # Add this record as a child of the parent |
| 260 | + if parentIdx >= 0 and parentIdx < gCallRecords.len: |
| 261 | + gCallRecords[parentIdx].children.add(result) |
| 262 | + |
| 263 | +proc printCallTree*(idx: int, depth: int = 0) = |
| 264 | + ## Recursively prints the call tree |
| 265 | + when defined(instrument): |
| 266 | + if idx < 0 or idx >= gCallRecords.len: |
| 267 | + return |
| 268 | + |
| 269 | + let record = gCallRecords[idx] |
| 270 | + let indent = " ".repeat(depth) |
| 271 | + let dur = record.totalTime.inSeconds |
234 | 272 | let color = |
235 | 273 | if dur < 1: fgGreen |
236 | 274 | elif dur < 2: fgYellow |
237 | 275 | else: fgRed |
238 | | - stdout.styledWrite(fgDefault, "[") |
239 | | - stdout.styledWrite(color, $(name)) |
240 | | - stdout.styledWriteLine(fgDefault, "] took " & $(ends)) |
241 | | - if traceStack: |
242 | | - for entry in getStackTraceEntries()[1..^2]: #Skips instrument and self |
243 | | - echo " ", entry.filename, ":", entry.line, " ", entry.procname |
| 276 | + |
| 277 | + stdout.styledWrite(fgDefault, indent & "[") |
| 278 | + stdout.styledWrite(color, record.name) |
| 279 | + stdout.styledWrite(fgDefault, "] ") |
| 280 | + stdout.styledWrite(color, $record.totalTime) |
| 281 | + if record.callCount > 1: |
| 282 | + stdout.styledWrite(fgMagenta, " (×" & $record.callCount & ")") |
| 283 | + stdout.writeLine("") |
| 284 | + |
| 285 | + # Print children |
| 286 | + for childIdx in record.children: |
| 287 | + printCallTree(childIdx, depth + 1) |
| 288 | + |
| 289 | +proc printInstrumentationReport*() = |
| 290 | + ## Prints the accumulated instrumentation data as a hierarchical tree |
| 291 | + when defined(instrument): |
| 292 | + if gCallRecords.len == 0: |
| 293 | + return |
| 294 | + |
| 295 | + echo "" |
| 296 | + stdout.styledWriteLine(fgCyan, "=== Instrumentation Report ===") |
| 297 | + |
| 298 | + # Print all root-level calls |
| 299 | + for idx, record in gCallRecords: |
| 300 | + if record.parent == -1: |
| 301 | + printCallTree(idx) |
| 302 | + |
| 303 | + stdout.styledWriteLine(fgCyan, "==============================") |
| 304 | + echo "" |
| 305 | + |
| 306 | +template measureTime*(name: static string, traceStack: bool, body: untyped) = |
| 307 | + when defined(instrument): |
| 308 | + # Get or create the record for this call |
| 309 | + let parentIdx = if gCallStack.len > 0: gCallStack[^1] else: -1 |
| 310 | + let recordIdx = getOrCreateRecord(name, parentIdx) |
| 311 | + |
| 312 | + # Push this call onto the stack |
| 313 | + gCallStack.add(recordIdx) |
| 314 | + inc gInstrumentDepth |
| 315 | + |
| 316 | + let starts = times.now() |
| 317 | + body |
| 318 | + let ends = times.now() - starts |
| 319 | + |
| 320 | + # Update the record |
| 321 | + gCallRecords[recordIdx].totalTime = gCallRecords[recordIdx].totalTime + ends |
| 322 | + gCallRecords[recordIdx].callCount += 1 |
| 323 | + |
| 324 | + # Pop from stack |
| 325 | + dec gInstrumentDepth |
| 326 | + discard gCallStack.pop() |
244 | 327 | else: |
245 | 328 | body |
246 | 329 |
|
|
0 commit comments