Skip to content

Introduce a fhirpath debug tracer #3210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: develop
Choose a base branch
from

Conversation

mmsmits
Copy link
Member

@mmsmits mmsmits commented Jul 9, 2025

Description

Introduce a capability to inject a debug trace into the fhirpath evaluation.
This will be used by systems that want to trace execution and debug what happened at each stage.
(such as in the fhirpath-lab)

Related issues

Closes|Fixes|Resolves [issue #].

Testing

There is specific unit testing to verify the captuing of the trace informtion

FirelyTeam Checklist

  • Update the title of the PR to be succinct and less than 50 characters
  • Mark the PR with the label breaking change when this PR introduces breaking changes

brianpos added 3 commits June 24, 2025 21:16
…ant (that's the base class of identifier)

Permits being able to differentiate in the expression tree between them.
only has minor impact on expression compilation, and no effect on runtime if no debugger injected.
@mmsmits mmsmits requested review from brianpos and Copilot July 17, 2025 08:11
Copilot

This comment was marked as outdated.

brianpos and others added 3 commits July 18, 2025 10:54
Only inject the debugger break when in debug mode.
This code should never be hit, so excluding the break is good. Intended to wake the developer if it ever hits here, as that means a new Expression type has been added that doesn't derived from one of the existing ones.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@brianpos brianpos changed the title Feature/bp fhirpath debugger Introduce a fhirpath debug tracer Jul 18, 2025
@brianpos brianpos marked this pull request as ready for review July 22, 2025 12:45
…al argument to the existing function.

(from a usage side, looks the same)
@brianpos
Copy link
Collaborator

brianpos commented Jul 22, 2025

This isn't ready for merging just yet. I've discovered that context.GetThat() isn't the input collection, it's actually identical to GetThis() - so isn't helping with my debug tracing.
In fact I don't see the purpose of getThat() at all, value is ALWAYS the same as getThis()
Interface is good, injection needs review.

Made lots of updates, it should be right, but curious if there are any performance impacts.
The tree is unchanged without the debug injected, just some tracking of the focus is returned by the delgate and that's about it.
And the change that only impacts substring of all things.

brianpos added 2 commits July 23, 2025 14:16
WrapWithPropNullForFocus - refactored the code in Wrap into the function - this caused the focus to be evaluted twice
@alexzautke
Copy link
Member

This isn't ready for merging just yet. I've discovered that context.GetThat() isn't the input collection, it's actually identical to GetThis() - so isn't helping with my debug tracing. In fact I don't see the purpose of getThat() at all, value is ALWAYS the same as getThis() Interface is good, injection needs review.

Made lots of updates, it should be right, but curious if there are any performance impacts. The tree is unchanged without the debug injected, just some tracking of the focus is returned by the delgate and that's about it. And the change that only impacts substring of all things.

Alright. Just let us know when you are ready.

@brianpos
Copy link
Collaborator

Alright. Just let us know when you are ready.

Sorry, I meant it's ready for you to review, though requesting you (or whoever) to be thorough, and happy to chat with whoever or do a call for a walk through.

@mmsmits mmsmits requested a review from Copilot July 23, 2025 07:58
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces debug tracing capability for FHIRPath expression evaluation, allowing systems to inject debug tracers to monitor and trace execution steps at each stage of expression processing.

  • Adds IDebugTracer interface for capturing FHIRPath expression evaluation steps
  • Updates FhirPathCompiler to accept optional debug tracers during compilation
  • Implements comprehensive debug tracing throughout the expression evaluation pipeline

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Hl7.Fhir.Base/FhirPath/IDebugTracer.cs Defines the debug tracer interface with TraceCall method
src/Hl7.Fhir.Base/FhirPath/DiagnosticsDebugTracer.cs Provides default implementation using System.Diagnostics.Trace
src/Hl7.Fhir.Base/FhirPath/FhirPathCompiler.cs Adds overloads accepting IDebugTracer parameter
src/Hl7.Fhir.Base/FhirPath/Expressions/EvaluatorVisitor.cs Integrates debug tracing wrapper into expression evaluation
src/Hl7.Fhir.Base/FhirPath/Expressions/Invokee.cs Updates delegate signature to support focus tracking for debug tracing
src/Hl7.Fhir.Base/FhirPath/Parser/Grammar.cs Updates parser to use IdentifierExpression instead of ConstantExpression
src/Hl7.FhirPath.R4.Tests/DebugTracerTests.cs Comprehensive unit tests validating debug tracer functionality

@mmsmits mmsmits requested review from Kasdejong and removed request for brianpos July 23, 2025 08:30
Copy link
Member

@Kasdejong Kasdejong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we are taking this in the wrong direction. This out parameter being ignored 9/10 times is bothering me. I feel like since our FP engine is built on immutability, saving this data on the fly should be a lot easier than it seems. After all, all invokees receive an old focus, and return a new one, but that does not make the old one any less valid. after the call, args[0] still contains the old focus. Or at least, it should.

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We aim to have nullability annotations for any new classes. If those could be added, that would be great.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added nullability.

IEnumerable<KeyValuePair<string, IEnumerable<ITypedElement>>> variables)
{
string exprName;
if (expr is IdentifierExpression ie)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this chain of ifs could be refactored a BUNCH. maybe exprName = expr switch and then a single trace after using the result...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The traces aren't all the same, but pretty close.
Refactored the if else out though

@@ -18,30 +18,49 @@

namespace Hl7.FhirPath.Expressions;

internal delegate FocusCollection Invokee(Closure context, IEnumerable<Invokee> arguments);
internal delegate FocusCollection Invokee(Closure context, IEnumerable<Invokee> arguments, out FocusCollection focus);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a massive change. I don't see how this is necessary when the focus should be in arguments[0]. Can we not just save it in some outer scope? I see this out param being ignored very often, which is probably a sign it should not be there.

If you REALLY feel like you need it, we should overload this somehow.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The out parameter is only used by the debug tracer. Which is only injected by the visitor when configured.
Rest of the time it does nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants