classdef (Hidden = true) StackProbeComponentRegistry < coder.profile.ProbeComponentRegistry
%

%   Copyright 2020-2023 The MathWorks, Inc.
    
    methods
        function this = StackProbeComponentRegistry(componentName,...
                componentRef,...
                targetWordSize,...
                maxIdLength, ...
                codeFolderRelative)
            this = this@coder.profile.ProbeComponentRegistry(componentName, componentRef, targetWordSize, [], maxIdLength, codeFolderRelative);
            this.registerProbeType(coder_profile_ProbeType.TASK_STACK_PROBE);
            this.registerProbeType(coder_profile_ProbeType.EXEC_STACK_PROBE);
            this.registerProbeType(coder_profile_ProbeType.OFFSET_STACK_PROBE);
            this.registerProbeType(coder_profile_ProbeType.CATCH_STACK_PROBE);
        end
        
        function id = requestTaskStackProbe(this, functionName, taskInfo, lFullComponentPath)

            if isempty(this.ComponentRef)
                this.ComponentRef = lFullComponentPath;
            else
                assert(strcmp(this.ComponentRef, lFullComponentPath),...
                    'The component path must remain consistent');
            end
            
            extFunctionName = this.getExtendedTaskName(functionName,...
                taskInfo.samplePeriod,...
                taskInfo.sampleOffset);
            
            id = this.requestSectionId(coder_profile_ProbeType.TASK_STACK_PROBE,...
                this.ComponentName,...
                extFunctionName,...
                taskInfo.taskName,...
                taskInfo.samplePeriod,...
                taskInfo.sampleOffset);
            
            % fill in local SectionIds
            probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.TASK_STACK_PROBE);
            this.addInternalSectionId(probeTypeName, id);
            
            this.checkMaxIdLimit(probeTypeName);
            
            % add probe to ProbeCalls
            this.instrumentationFnc(coder_profile_ProbeType.TASK_STACK_PROBE, id);
            
            lModelTraceInfo.CodeName = extFunctionName;
            lModelTraceInfo.CallSiteSids = {};
            lModelTraceInfo.CallSiteNames = {taskInfo.taskName};
            lModelTraceInfo.ProfSecsSampleTime = [taskInfo.samplePeriod taskInfo.sampleOffset];
            
            this.updateProfilingSections(probeTypeName, lModelTraceInfo);
        end

        function [sectionId, profileFcns] = insertCatchStackProbe(this, fileName, lineNum, retProbeId)
            probeType = coder_profile_ProbeType.CATCH_STACK_PROBE;
            sectionId = this.requestSectionId(probeType, this.ComponentName, fileName, lineNum, retProbeId);
            % fill in local SectionIds
            probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.CATCH_STACK_PROBE);
            this.addInternalSectionId(probeTypeName, sectionId);
            % add probe to ProbeCalls
            profileFcns = this.instrumentationFnc(coder_profile_ProbeType.CATCH_STACK_PROBE, sectionId);
        end
        
        function [sectionId, profileFcns] = insertOffsetStackProbe(this, codeName)
            % generate section id
            probeType = coder_profile_ProbeType.OFFSET_STACK_PROBE;
            sectionId = this.requestSectionId(probeType, codeName);
            % fill in local SectionIds
            probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.OFFSET_STACK_PROBE);
            this.addInternalSectionId(probeTypeName, sectionId);
            % add probe to ProbeCalls
            profileFcns = this.instrumentationFnc(coder_profile_ProbeType.OFFSET_STACK_PROBE, sectionId);
        end

        function probeFncCalls = instrumentationFnc(this, lProbeType, lSectionId)
            lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
            probeRealInstr =...
                this.getProbeInstrumentationFcn(...
                lProbeType,...
                lSectionId);
            
            probeFncCalls =...
                arrayfun(@(x) strcat(x, ['_' this.ComponentName]),...
                probeRealInstr);
            probeFncCalls = ...
                arrayfun(@(x) coder.profile.private.createProbeIdentifier...
                (x.name, this.MaxIdLength),...
                cell2struct(probeFncCalls, {'name'}, 1),...
                'UniformOutput', false);
            
            % check to see if we already have this name for the probe type
            if this.ProbeCalls.isKey(lProbeTypeName)
                % safety check
                vals = this.ProbeCalls(lProbeTypeName);
                assert(isequal(vals{1}, probeFncCalls),...
                    'The wrapper probe calls should be same across the same probe type');
                assert(isequal(vals{2}, probeRealInstr),...
                    'The real instrumentation probe calls should be same across the same probe type');
            else
                vals = cell(1,2);
                vals{1} = probeFncCalls;
                vals{2} = probeRealInstr;
                this.ProbeCalls(lProbeTypeName) = vals;
            end
        end

        % insert an EXEC_STACK_PROBE using EDG instrumentation
        function [sectionId, profileFcns] = insertExecStackProbe...
                           (this, codeName, fileName, lineNumStart, lineNumStop)

            probeType = coder_profile_ProbeType.EXEC_STACK_PROBE;

            sectionId = this.requestSectionId(probeType, codeName, fileName, this.ComponentName, lineNumStart, lineNumStop);

            % fill in local SectionIds
            probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.EXEC_STACK_PROBE);
                        
            callSiteNameArr = {codeName};
            traceInfo = struct('CodeName', codeName, 'CallSiteSids', {''}, 'CallSiteNames', {callSiteNameArr});
            profileFcns = this.addProbeIdentifier(probeType, probeTypeName, sectionId, traceInfo);
            
            if ~isequal(lineNumStart, 0)
                if ~this.ProfSecsSourceFile.isKey(fileName)
                    this.ProfSecsSourceFile(fileName) = {};
                end
                entries = this.ProfSecsSourceFile(fileName);
                entries{end+1} = { sectionId, { lineNumStart, lineNumStop } };
                this.ProfSecsSourceFile(fileName) = entries;
            end
        end
    end
    
    methods (Access = private)
        function extendedTaskName = getExtendedTaskName(~, taskName, taskPeriod, taskOffset)
            % this function returns an extended version of the function
            % name as a combination of its name, sample time and offset.
            % This name will appear on SDI
            extendedTaskName = '';
            if ~isempty(taskPeriod) && taskPeriod>0 && taskPeriod~=Inf
                extendedTaskName = sprintf('[%g %g]', taskPeriod, taskOffset);
            end
            extendedTaskName = strtrim(sprintf('%s %s', taskName, extendedTaskName));
        end
    end
end
