classdef ExecutionTimeProbeTarget < coder.profile.TargetInterface
%

% Copyright 2013-2023 The MathWorks, Inc.

    properties (Constant, Access=protected)
        TaskProbe    = 0;
        ExecProbe    = 1;
        FilterProbe  = 2;
        OverrunProbe = 3;
    end

    properties (Access = private)
        IsXpcMode;
        IsExtReadStoreManagement; % this property states if timer read and
                                  % storage in buffer are done externally
        ProfilingStartFnc;
        ProfilingEndFnc;
        ProbeType;

        IsOverheadFilterEnabled;
        
        % if true, samples are uploaded immediately. They are stored on target
        % until the end otherwise
        RealTimeUpload;
    end
    
    methods (Access = protected)
        function isXpc = getIsXpcMode(this)
            if isempty(this.IsXpcMode)
                this.IsXpcMode = false;
                if ~isempty(this.ProfilingTimer)
                    this.IsXpcMode =...
                        this.ProfilingTimer.getIsXpcMode;
                end
            end
            
            isXpc = this.IsXpcMode;
        end
        
        function [timer_expr_prefix, timer_expr_suffix] = getTimerDirection(this)
            if strcmp(this.ProfilingTimer.getCountDirection,'up')
                timer_expr_prefix = '';
                timer_expr_suffix = '';
            else
                timer_expr_prefix = ' ~(';
                timer_expr_suffix = ')';
            end
        end
        
        function isExtReadStoreManagement = getIsExtReadStoreManagement(this)
            if isempty(this.IsExtReadStoreManagement)
                this.IsExtReadStoreManagement = getIsXpcMode(this) || ...
                    ~isempty(this.GlobalRegistry.AtomicReadStoreFcnName);
            end
            isExtReadStoreManagement = this.IsExtReadStoreManagement;
        end
        
        function targetWriteOverheadSetting(this, writer)
            lTimerType = this.ProfilingTimer.getTimerCType;
            writer.wBlockStart('void xilProfilingOverheadSetting(%s tskVal, %s fcnVal, %s frzVal)', ...
                lTimerType, lTimerType, lTimerType);
            writer.writeLine('xsd_xil_timer_overhead_task = tskVal;');
            writer.writeLine('xsd_xil_timer_overhead_function = fcnVal;');
            writer.writeLine('xsd_xil_timer_overhead_freezing = frzVal;');
            writer.writeLine('xsd_xil_timer_overhead_total = 0;');
            writer.wBlockEnd;
        end

        function idContainerCType = getSectionIdContainerCType(this)
            % If the timer type is larger than the section id type, we put 
            % the section id for on-target metrics in a container.
            % This allows the uploaded data to be located in statically 
            % allocated contiguous memory without having to handle 
            % alignment issues for the timer data
                    
            idContainerCType =  this.IdentifierCDataType;
            if ~isempty(this.ProfilingTimer)
                timerWordLength = this.ProfilingTimer.getTimerWordLength;
                lIdentifierTypeSize = ...
                    coder.profile.ExecTimeConfig.getIdTypeSize...
                    (this.TargetWordSize);
                if timerWordLength > lIdentifierTypeSize
                    idContainerCType = sprintf('uint%d_T', timerWordLength);
                end
            end
        end        

        
        function targetWriteSectionLocalFcnPrototypes(this, writer, executed)
            if ~executed && ~this.GlobalRegistry.RequireXCPSupport
                if ~isempty(this.ProfilingTimer)
                    writer.wComment('Uploads data');
                    if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                        writer.writeLine('void xilUploadProfilingData(%s sectionId, %s timerVal);',...
                                         this.IdentifierCDataType,...
                                         this.ProfilingTimer.getTimerCType);
                    else
                        writer.writeLine('void xilUploadProfilingData(%s sectionId);',...
                                         this.IdentifierCDataType);
                    end
                    writer.newLine;
                end
                writer.newLine;
                % for XCP targets, the upload method is called directly
                % instead of using xilUploadProfilingData as intermediary
            end
        end
        
        function targetWriteSectionStorage(this, writer, codeDescriptor)
            writer.newLine;
                unusedParamMacro = {
                    '/* A function parameter may be intentionally unused */'
                    '#ifndef UNUSED_PARAMETER  '
                    '# if defined(__LCC__)  '
                    '#   define UNUSED_PARAMETER(x)'
                    '# else  '
                    '#   define UNUSED_PARAMETER(x) (void) (x)  '
                    '# endif  '
                    '#endif  '
                                   };
                writer.writeCellLines(unusedParamMacro);
                writer.newLine;

            if isempty(this.ProfilingTimer)
                return;
            end
                               
            lTimerType = this.ProfilingTimer.getTimerCType(codeDescriptor);
                        
            if ~this.getIsExtReadStoreManagement
                lTimerType = this.ProfilingTimer.getTimerCType;
                lIdType = this.IdentifierCDataType;
                lSectionIdContainerCType = this.getSectionIdContainerCType;
                if ~strcmp(lSectionIdContainerCType, lIdType)
                    writer.wLine('#define SIZEOF_SECTION_ID_CONTAINER sizeof(%s)', ...
                                 lSectionIdContainerCType);
                end
                writer.wLine('#define SIZEOF_TIMER_TYPE sizeof(%s)', lTimerType);
            end            
            
            if this.SingleThreadTiming
                writer.writeLine('static %s xsd_xil_timer_corrected = 0;', ...
                    lTimerType);
                writer.newLine;
                
                writer.writeLine('static %s xsd_xil_timer_unfreeze = 0;', ...
                    lTimerType);
                writer.newLine;

                writer.writeLine('static %s xsd_xil_freezing_busy = 0;', ...
                                 this.IdentifierCDataType);
                writer.newLine;
                
                if this.IsOverheadFilterEnabled
                    writer.wComment('Variables to store the estimated overhead of the instrumentation API');
                    writer.writeLine('static %s xsd_xil_timer_overhead_freezing = 0;', ...
                        lTimerType);
                    writer.writeLine('static %s xsd_xil_timer_overhead_task = 0;', ...
                        lTimerType);
                    writer.writeLine('static %s xsd_xil_timer_overhead_function = 0;', ...
                        lTimerType);
                    writer.wComment('Variable to account the total generated overhead since last freezing');
                    writer.writeLine('static %s xsd_xil_timer_overhead_total = 0;', ...
                        lTimerType);
                    writer.newLine;
                end
                
                writer.newLine
            end

            writer.newLine;
        end
        
        function writeSectionUploadProfilingData(this, writer, targetCollectDataFcnName)
            lIdType = this.IdentifierCDataType;
            
            sectionIdVar = 'sectionId';
            timerVar = 'xsd_xil_timer_corrected';
            if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                timerVar = 'timerVal';
                fcnSignature = sprintf('void xilUploadProfilingData(%s %s, %s %s)',...
                                       lIdType,...
                                       sectionIdVar,...
                                       this.ProfilingTimer.getTimerCType,...
                                       timerVar);
            else
                fcnSignature = sprintf('void xilUploadProfilingData(%s %s)', ...
                                       lIdType,...
                                       sectionIdVar);
            end
            
            writer.wBlockStart('%s', fcnSignature);
            
            if this.getIsExtReadStoreManagement
                % this code is executed if AtomicReadStoreFcnName is not empty
                % or XpcMode is active
                if ~this.RealTimeUpload
                    writer.wComment('No implementation required for this method');
                    writer.writeLine('UNUSED_PARAMETER(%s);', sectionIdVar);
                elseif this.getIsXpcMode
                    timerFunc =  this.ProfilingTimer.getReadTimerExpression;
                    % We need to insert the sectionId argument
                    timerFunc = regexprep(timerFunc, '\(\)', ['\(' sectionIdVar '\)']);
                    writer.writeLine('%s;', timerFunc);
                elseif ~isempty(this.GlobalRegistry.AtomicReadStoreFcnName)
                    % based on how this.getIsExtReadStoreManagement is computed, this
                    % code is executed if AtomicReadStoreFcnName is not empty
                    timerFunc = this.GlobalRegistry.AtomicReadStoreFcnName;
                    writer.writeLine('%s(%s);', timerFunc, sectionIdVar);
                else
                    assert(false, 'No possible to generate external management of timer read/store');
                end
            else
                lCodeInstrSizeCDataType = ...
                    coder.profile.ExecTimeConfig.getCodeInstrNumElsCDataType...
                    (this.TargetWordSize);
                writer.writeLine...
                    ('%s((void *)(&%s), (%s)(SIZEOF_TIMER_TYPE), %s);', ...
                     targetCollectDataFcnName, ...
                     timerVar,...
                     lCodeInstrSizeCDataType, ...
                     sectionIdVar);
            end
                        
            writer.wBlockEnd();
            writer.newLine;

        end
        
        function writeSectionProfilingStartEnd(this, isStart, lProbeType, ...
                writer, fcnName, codeDescriptor)
            
            fcnSignature = sprintf('#define %s(id)', fcnName);
            
            if isempty(this.ProfilingTimer)
                writer.writeLine('%s {UNUSED_PARAMETER(id);}', fcnSignature);
                writer.newLine;
                return;
            end
            
            if isStart
                switch (lProbeType)
                    case this.TaskProbe
                        writer.wLine('/* Tic/Toc methods for task profiling */');
                    case this.ExecProbe
                        writer.wLine('/* Tic/Toc methods for function profiling */');
                    case this.FilterProbe
                        writer.wLine('/* Tic/Toc methods for filtering */');
                end
            end
            writer.wLine('%s { \\ ', fcnSignature);
            switch (lProbeType)
                case this.TaskProbe
                    this.writeSectionTaskProbeBodyStartEnd(writer, isStart, codeDescriptor);
                case this.ExecProbe
                    this.writeSectionExecProbeBodyStartEnd(writer, isStart);
                case this.FilterProbe
                    this.writeSectionFilterProbeBodyStartEnd(writer, isStart);
                case this.OverrunProbe
                    assert(isStart, 'Only start method is supported');
                    this.writeSectionOverrunProbeBodyStartEnd(writer);
            end
            writer.wLine('}');
            writer.newLine;
        end
        
        function writeSectionTaskProbeBodyStartEnd (this, writer, isStart, ...
                codeDescriptor)

            lTimerType = this.ProfilingTimer.getTimerCType(codeDescriptor);
            lIdType = this.IdentifierCDataType;
            % negate the section ID if needed
            if ~isStart && this.RealTimeUpload
                writer.wLine('  %s sectionIdNeg = id; \\ ', lIdType);
                writer.wLine('  sectionIdNeg = ~sectionIdNeg; \ ');
            end
            % variable to store/upload
            if ~this.getIsExtReadStoreManagement
                if this.SingleThreadTiming
                    timerValueExpr = 'xsd_xil_timer_corrected';
                else
                    timerValueExpr = 'timer_value';
                    writer.wLine('  %s %s; \\  ', lTimerType, timerValueExpr);
                end
            end
            % special operations for end method
            if ~isStart
                if this.IsOverheadFilterEnabled
                    % add task-level overhead to total overhead
                    writer.wLine('  xsd_xil_timer_overhead_total = xsd_xil_timer_overhead_total + xsd_xil_timer_overhead_task; \ ');
                end
                % freeze timer as no more profiled code after this point
                if ~this.GlobalRegistry.RequireXCPSupport
                    writer.writeLine('  xilProfilingTimerFreezeInternal(); \ ');
                end
            end
            % get timer value if the read/store isn't delegated to an
            % external method
            if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                timerFunc =  this.ProfilingTimer.getReadTimerExpression;
                [timer_expr_prefix, timer_expr_suffix] = this.getTimerDirection();
                writer.writeLine('  %s = %s(%s) %s%s; \\ ', ...
                                 timerValueExpr,...
                                 timer_expr_prefix, ...
                                 lTimerType, timerFunc, timer_expr_suffix);
            end
            % process time value
            if ~this.RealTimeUpload
                % capture mode: store in internal buffer
                if isStart
                    writer.wLine('  %s(id); \\ ', ...
                        coder.profile.CaptureProbeTarget.StartInternalMethod);
                else
                    writer.wLine('  %s(id); \\ ', ...
                        coder.profile.CaptureProbeTarget.EndInternalMethod);
                end
            else
                % standard SIL/PIL: send execution profiling data to host
                if isStart
                    sectionIdVar = 'id';
                else
                    sectionIdVar = 'sectionIdNeg';
                end
                if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                    writer.wLine('  xilUploadProfilingData(%s, %s); \\ ', sectionIdVar, timerValueExpr);
                elseif this.GlobalRegistry.RequireXCPSupport
                    timerFunc = this.GlobalRegistry.AtomicReadStoreFcnName;
                    writer.wLine('  %s(%s); \\ ', timerFunc, sectionIdVar);
                else
                    writer.wLine('  xilUploadProfilingData(%s); \\ ', sectionIdVar);
                end
            end
            % unfreeze timer
            if isStart && ~this.GlobalRegistry.RequireXCPSupport
                writer.writeLine('  xilProfilingTimerUnFreezeInternal(); \ ');
            end
        end
        
        function writeSectionExecProbeBodyStartEnd (this, writer, isStart)
            if ~isStart && this.RealTimeUpload
                lIdType = this.IdentifierCDataType;
                writer.wLine('  %s sectionIdNeg = id; \\ ', lIdType);
                writer.wLine('  sectionIdNeg = ~sectionIdNeg; \ ');
            end
            
            if ~this.getIsExtReadStoreManagement
                if this.SingleThreadTiming
                    timerValueExpr = 'xsd_xil_timer_corrected';
                else
                    timerValueExpr = 'timer_value';
                    lTimerType = this.ProfilingTimer.getTimerCType;
                    writer.wLine('  %s %s; \\ ', lTimerType, timerValueExpr);
                end
            end
            
            if this.IsOverheadFilterEnabled && ~isStart
                % writer.wComment('Add function-level overhead to total overhead');
                writer.wLine('  xsd_xil_timer_overhead_total = xsd_xil_timer_overhead_total + xsd_xil_timer_overhead_function; \ ');
            end

            if ~this.GlobalRegistry.RequireXCPSupport
                writer.writeLine('  xilProfilingTimerFreezeInternal(); \ ');
            end
            
            if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                timerFunc =  this.ProfilingTimer.getReadTimerExpression;
                [timer_expr_prefix, timer_expr_suffix] = this.getTimerDirection();
                writer.writeLine('  %s = %s(%s) %s%s; \\ ', ...
                    timerValueExpr,...
                    timer_expr_prefix, ...
                    lTimerType, timerFunc, timer_expr_suffix);
            end
            
            if isStart || ~this.RealTimeUpload
                sectionIdVar = 'id';
            else
                sectionIdVar = 'sectionIdNeg';
            end

            if ~this.RealTimeUpload
                if isStart
                    writer.writeLine('  %s(%s); \\ ', ...
                        coder.profile.CaptureProbeTarget.StartInternalMethod, ...
                        sectionIdVar);
                else
                    writer.writeLine('  %s(%s); \\ ', ...
                        coder.profile.CaptureProbeTarget.EndInternalMethod, ...
                        sectionIdVar);
                end
            else
                % writer.wComment('Send execution profiling data to host');
                if ~this.getIsExtReadStoreManagement && ~this.SingleThreadTiming
                    writer.writeLine('  xilUploadProfilingData(%s, %s); \\ ', sectionIdVar, timerValueExpr);
                elseif this.GlobalRegistry.RequireXCPSupport
                    timerFunc = this.GlobalRegistry.AtomicReadStoreFcnName;
                    writer.wLine('  %s(%s); \\ ', timerFunc, sectionIdVar);
                else
                    writer.wLine('  xilUploadProfilingData(%s); \\ ', sectionIdVar);
                end
            end
            
            if ~this.GlobalRegistry.RequireXCPSupport
                writer.wLine('  xilProfilingTimerUnFreezeInternal(); \ ');
            end
        end
        
        function writeSectionFilterProbeBodyStartEnd (this, writer, isStart)
            writer.wLine('UNUSED_PARAMETER(id); \ ');
            if ~this.GlobalRegistry.RequireXCPSupport
                if isStart
                    writer.wLine('  xilProfilingTimerFreezeInternal(); \ ');
                    if this.SingleThreadTiming
                        writer.wLine('  xsd_xil_freezing_busy = 1; \');
                    end
                else
                    if this.SingleThreadTiming
                        writer.wLine('  xsd_xil_freezing_busy = 0; \');
                    end
                    if this.IsOverheadFilterEnabled
                        % writer.wComment('Add freezing overhead to total overhead');
                        writer.wLine('  xsd_xil_timer_overhead_total = xsd_xil_timer_overhead_total + xsd_xil_timer_overhead_freezing; \ ');
                    end
                    writer.writeLine('  xilProfilingTimerUnFreezeInternal(); \ ');
                end
            end
        end

        function writeSectionOverrunProbeBodyStartEnd(this, writer)
            assert(this.GlobalRegistry.RequireXCPSupport, ...
                'This class of probes must be used only with XCP targets');
            
            sectionIdVar = 'id';
            
            timerFunc = this.GlobalRegistry.AtomicReadStoreFcnName;
            writer.writeLine('%s(%s); \\ ', timerFunc, sectionIdVar);
        end
        
        function writeSectionProfilingTimerFreezeFcns(this, writer)
            % write function definition for timer freezing/unfreezing

            if this.SingleThreadTiming
                type = this.ProfilingTimer.getTimerCType;
                timerFunc =  this.ProfilingTimer.getReadTimerExpression;
                % add brief description
                writer.wLine('/* The internal freeze and unfreeze methods cannot be nested.');
                writer.wLine(' * The customer-visible implementation avoids nesting problems.');
                writer.wLine(' * Update the value of the corrected timer to exclude time');
                writer.wLine(' * spent in the instrumentation code');
                writer.wLine(' */');
                % freeze method: update timer_freeze and timer_corrected
                writer.wLine('#define xilProfilingTimerFreezeInternal() { \ ');
                % - if filtering is on, take previous value
                if this.IsOverheadFilterEnabled
                    writer.wLine('  %s xsd_xil_timer_corrected_previous = xsd_xil_timer_corrected; \\ ', type);
                end
                % - update corrected timer
                timer_read_expr = sprintf('((%s)(%s))', type, timerFunc);
                if strcmp(this.ProfilingTimer.getCountDirection(),'up')
                    timer_expr_prefix = '+';
                else
                    timer_expr_prefix = '-';
                end
                writer.wLine('  xsd_xil_timer_corrected = xsd_xil_timer_corrected %s (%s - xsd_xil_timer_unfreeze); \\ ', ...
                    timer_expr_prefix, timer_read_expr);
                % - if filtering is on, remove overhead
                if this.IsOverheadFilterEnabled
                    writer.writeLine('  if ((xsd_xil_timer_corrected - xsd_xil_timer_corrected_previous) > xsd_xil_timer_overhead_total) \');
                    writer.writeLine('    xsd_xil_timer_corrected = xsd_xil_timer_corrected - xsd_xil_timer_overhead_total; \ ');
                    writer.writeLine('  else \ ');
                    writer.writeLine('    xsd_xil_timer_corrected = xsd_xil_timer_corrected_previous; \ ');
                    writer.writeLine('  xsd_xil_timer_overhead_total = 0; \ ');
                end
                writer.wLine('}');
                writer.newLine;
                % unfreeze function: update unfreeze timer
                writer.wLine('#define xilProfilingTimerUnFreezeInternal() { \ ');
                writer.writeLine('  xsd_xil_timer_unfreeze = (%s) (%s); \\ ', type, timerFunc);
                writer.wLine('}');
                writer.newLine;
                % freeze function
                writer.wBlockStart('void xilProfilingTimerFreeze(void)');
                writer.wBlockStart('if (xsd_xil_freezing_busy == 0)');
                writer.wLine('xilProfilingTimerFreezeInternal();');
                writer.wBlockEnd();
                writer.wBlockEnd();
                writer.newLine;
                % unfreeze function
                writer.wBlockStart('void xilProfilingTimerUnFreeze(void)');
                writer.wBlockStart('if (xsd_xil_freezing_busy == 0)');
                writer.wLine('xilProfilingTimerUnFreezeInternal();');
                writer.wBlockEnd();
                writer.wBlockEnd();
                writer.newLine;
            else
                % for real-time execution, these methods have an empty 
                % implementation
                if ~isempty(this.ProfilingTimer)
                    writer.wComment('For real-time, multitasking case this function is stubbed out.');
                end                
                writer.writeLine('#define xilProfilingTimerFreezeInternal() {}');
                writer.writeLine('void xilProfilingTimerFreeze(void) {}');
                writer.writeLine('#define xilProfilingTimerUnFreezeInternal() {}');
                writer.writeLine('void xilProfilingTimerUnFreeze(void) {}');
                writer.newLine;
            end
        end

        function targetWriteSectionFcnsFinal(this, writer, targetCollectDataFcnName, ...
                executed, codeDescriptor)
            if ~executed && ~this.GlobalRegistry.RequireXCPSupport
                if ~isempty(this.ProfilingTimer)
                    this.writeSectionUploadProfilingData(writer, targetCollectDataFcnName);
                end
            
                this.writeSectionProfilingTimerFreezeFcns(writer);
            end
            
            % profiling function for start of regular section
            isStart = true;
            this.writeSectionProfilingStartEnd(isStart, this.ProbeType, writer, ...
                this.ProfilingStartFnc, codeDescriptor);
            
            % profiling function for end of regular section
            if ~isempty(this.ProfilingEndFnc)
                isStart = false;
                this.writeSectionProfilingStartEnd(isStart, this.ProbeType, writer, ...
                    this.ProfilingEndFnc, codeDescriptor);
            end
        end
    end
    
    methods (Access = public, Static)
        function obj = getTaskProbeTarget (lStartFnc, lEndFnc)
            obj = coder.profile.ExecutionTimeProbeTarget(lStartFnc, lEndFnc, ...
                        coder.profile.ExecutionTimeProbeTarget.TaskProbe);
        end

        function obj = getFilterProbeTarget (lStartFnc, lEndFnc)
            obj = coder.profile.ExecutionTimeProbeTarget(lStartFnc, lEndFnc, ...
                        coder.profile.ExecutionTimeProbeTarget.FilterProbe);
        end

        function obj = getExecutionProbeTarget (lStartFnc, lEndFnc)
            obj = coder.profile.ExecutionTimeProbeTarget(lStartFnc, lEndFnc, ...
                        coder.profile.ExecutionTimeProbeTarget.ExecProbe);
        end

        function obj = getOverrunProbeTarget (lStartFnc)
            obj = coder.profile.ExecutionTimeProbeTarget(lStartFnc, '', ...
                        coder.profile.ExecutionTimeProbeTarget.OverrunProbe);
        end
    end
   
    methods (Access = public)
        function this = ExecutionTimeProbeTarget(lStartFnc, lEndFnc, lProbeType)
            this.ProfilingStartFnc = lStartFnc;
            this.ProfilingEndFnc = lEndFnc;
            this.ProbeType = lProbeType;
        end
        
        function targetWriteSectionExportedAtomicFcn(this, writer, collect)
            if ~isempty(collect)
                writer.wComment('Atomic function to read timer and upload data point');
                writer.writeLine('void %s(%s sectionId);', collect, this.IdentifierCDataType);
                writer.newLine;
            end
        end
        
        function init (this, lTargetWordSize, lSingleThreadTiming, ...
                lProfilingTimer, lGlobalRegistry, codeDescriptor)
            init@coder.profile.TargetInterface(this, lTargetWordSize, ...
                lSingleThreadTiming, lProfilingTimer, lGlobalRegistry, ...
                codeDescriptor);
            this.IsOverheadFilterEnabled = this.GlobalRegistry.FilterProfilingOverhead;
            this.RealTimeUpload = this.GlobalRegistry.UploadDataInRealTime;
            
            if this.IsOverheadFilterEnabled
                assert(this.SingleThreadTiming, ...
                    'The overhead is estimated only on Single-Thread models');
            end
        end
        
        % implementation of abstract methods
        function targetWriteProbeExportedFcnPrototypes(this, writer, executed)
            this.targetWriteSectionLocalFcnPrototypes(writer, executed);
            writer.newLine;
            
            if ~executed && ~this.GlobalRegistry.RequireXCPSupport
                writer.wComment('Pause/restart the timer while running code associated with storing and uploading the data.');
                writer.writeLine('void xilProfilingTimerFreeze(void);');
                writer.writeLine('void xilProfilingTimerUnFreeze(void);');
                writer.newLine;
                
                if this.IsOverheadFilterEnabled && ~this.GlobalRegistry.RunOverheadBenchmark
                    lTimerType = this.ProfilingTimer.getTimerCType;
                    writer.wComment('Manually specify instrumentation overhead');
                    writer.writeLine('void xilProfilingOverheadSetting(%s, %s, %s);', ...
                        lTimerType, lTimerType, lTimerType);
                end
            end
        end
        
        function targetWriteSectionFcns(this, writer, targetCollectDataFcnName, ...
                executed, codeDescriptor)
            if ~executed
                this.targetWriteSectionStorage(writer, codeDescriptor);

                if this.IsOverheadFilterEnabled && ~this.GlobalRegistry.RunOverheadBenchmark
                    this.targetWriteOverheadSetting(writer);
                end
            end
            this.targetWriteSectionFcnsFinal(writer, targetCollectDataFcnName, ...
                executed, codeDescriptor);
        end
        
        function targetWriteInit(~, ~) 
        end
    end
end

% LocalWords:  Xpc XCP extmode
