Skip to content

Generate timer results XML

This operation transforms the flat, semicolon-delimited timer dictionary into a hierarchical result tree and serializes that tree as XML. It is the library's primary reporting path: it is how a caller who has been starting and stopping timers throughout a run turns the accumulated data into a document that can be inspected, logged, or archived.

Field Value
Operation ID CT-OP-004
Trigger Caller invokes CodeTimer.Results() or CodeTimer.Results(bool includeTimeSpans)
Inputs Optional includeTimeSpans flag
Outputs XDocument rooted at <Timer>, or null when no timer data exists
Primary components CodeTimer.Results, CodeTimerResult.CodeTimerResult, CodeTimerResult.CreateItems, CodeTimerResult.OutputXml, CodeTimerResult.RecurseXml
Entry point SAIF.Framework.CodeTimer\SAIF.Framework.CodeTimer\CodeTimer.cs

Steps

  1. Normalize the call to CodeTimer.Results(bool includeTimeSpans) — the parameterless overload simply delegates to Results(false), so time-span detail is opt-in rather than the default.
  2. Check whether the static data dictionary contains any entries at all.
  3. If the dictionary is empty, return null immediately rather than constructing and returning an empty <Timer> document.
  4. Construct a new CodeTimerResult(data), whose constructor immediately calls CreateItems() to build the reporting tree before any XML is produced.
  5. Within CreateItems(), iterate every CodeTimerEntry currently in the dictionary.
  6. If any entry is still marked as running, abandon the reporting attempt and throw under BR-003 — a report is only meaningful once every timer it would summarize has actually finished.
  7. Split each entry's ;-delimited timer name into its component parts, and walk or create the corresponding CodeTimerItem nodes so the flat key space becomes a proper parent/child tree.
  8. Populate the leaf node that matches the entry's full key with its elapsed time, quantity, start/stop timestamps, shortest/longest span, and span history; populate any intermediate node created purely for grouping with sentinel values such as Elapsed = -1 so it can be distinguished from a real, measured timer.
  9. Call OutputXml(includeTimeSpans), which creates a new XDocument with a single <Timer> root element to hold the serialized tree.
  10. Recursively serialize each top-level CodeTimerItem through RecurseXml(...), which decides per node which attributes are worth emitting.
  11. Emit the short, long, avg, and qty attributes only when the item has been measured more than once (Qty > 1); a single-execution timer has no meaningful range or average to report (BR-006).
  12. Emit the ms attribute only when the item's elapsed value is greater than zero, so grouping-only nodes stay free of a misleading duration (BR-006).
  13. Emit <span> child elements only when the caller requested includeTimeSpans, the item recorded more than one span, and each individual span's computed duration is greater than zero milliseconds — a single span or a zero-length span carries no additional signal (BR-006).
  14. Recurse into an item's children only when that item both has children and has a positive Elapsed value; once every reachable node has been visited, return the completed XDocument to the caller.

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start([Results request]) --> HasData{Any timer data?}
    HasData -->|No| NoData([Return null])
    HasData -->|Yes| BuildResult[Construct CodeTimerResult]
    BuildResult --> Running{Any entry still running?}
    Running -->|Yes| Error([Throw running-timer exception])
    Running -->|No| BuildTree[Create CodeTimerItem tree]
    BuildTree --> Serialize[Serialize XML output]
    Serialize --> EndOk([Return XDocument])

Failure and edge-case behavior

  • Requesting results while any timer in the store is still running throws under BR-003 — the caller must ensure every started timer has been stopped first.
  • Results() returns null, not an empty document, when no timer entries have ever been captured; callers must check for null rather than assuming an XDocument is always returned.
  • Even with includeTimeSpans set to true, a timer that was only ever started and stopped once produces no <span> elements, because RecurseXml requires more than one recorded span before it will serialize any (BR-006).
  • Intermediate parent nodes that exist only to group child timers under a shared key prefix do not recurse into their children unless that parent node itself also has a positive Elapsed value, which can leave otherwise-valid child measurements unreported if the grouping node was never itself timed.

Sources

  • SAIF.Framework.CodeTimer\SAIF.Framework.CodeTimer\CodeTimer.cs
  • SAIF.Framework.CodeTimer\SAIF.Framework.CodeTimer\CodeTimerEntry.cs
  • SAIF.Framework.CodeTimer\SAIF.Framework.CodeTimer\CodeTimerItem.cs
  • SAIF.Framework.CodeTimer\SAIF.Framework.CodeTimer\CodeTimerResult.cs