classdef (Hidden = true) ProbeGlobalRegistry < handle
%

% Copyright 2013-2020 The MathWorks, Inc.
   
    properties (Access = private, Transient = true)
        % Absolute path to anchor folder must not be serialized
        AnchorFolder;
    end
        
    properties (Access = private)
        ComponentRegistries;
        RegistryProbeTypesCount;
        RegistryOffsets;
        GroupProbeTypesCount;
        TotalProbes;
        
        RegistryValuesMin;
        RegistryValuesMax;
        RelPathToAnchor;
    end
    
    properties (Access = public)
        SourceFileTargetInterface;
        HeaderFileTargetInterface;
        ProfilingTimer;
        TargetCollectDataFcnName;
        AtomicReadStoreFcnName;
        SplitAtomicReadStoreFcn = false;
        SingleThreadTiming;
        
        Model;
        SingleTaskModel = false;

        % filter the overhead introduced from the profiling API
        FilterProfilingOverhead;
        % run a benchmark to estimate the profiling API overhead
        RunOverheadBenchmark;
        
        % OnTarget mode not active
        UploadDataInRealTime = true;
        % Number of time probes (TASK_TIME_PROBE and EXEC_TIME_PROBE)
        % Used for OnTarget mode
        TotalTimeProbe = 0;
        
        InlineGeneratedCode = false;
        
        % this enables the generation of the buffer management code for external
        % mode via XCP
        RequireXCPSupport = false;
        XCPInstrumentedFolder;
        AddXCPHeaderFiles = [];
        AddXCPSourceFiles = [];
        XCPCoreIDFcn = '';
        XCPThreadIDFcn = '';
        XCPEventID;
        XCPBufferConfig;
    end

    properties (Access = public, Hidden = true)
        TimerHWCounterUnit = [];
    end
    
    properties (Constant)
        XCPMainSourceFile = 'ext_mode_profiling.c';
        XCPMainHeaderFile = 'ext_mode_profiling.h';
    end
    
    % set and get methods on properties
    methods
        function set.SourceFileTargetInterface(this, value)
            this.SourceFileTargetInterface = value;
        end
        
        function value = get.SourceFileTargetInterface(this)
            value = this.SourceFileTargetInterface;
        end
        
        function set.HeaderFileTargetInterface(this, value)
            this.HeaderFileTargetInterface = value;
        end
        
        function value = get.HeaderFileTargetInterface(this)
            value = this.HeaderFileTargetInterface;
        end
        
        function set.ProfilingTimer(this, lTimer)
            if isa(lTimer, 'coder.profile.Timer')
                % Backwards compatibility
                this.ProfilingTimer = coder.profile.private.TimerWrap(lTimer);
            else
                this.ProfilingTimer = lTimer;
            end
        end
        
        function value = get.ProfilingTimer(this)
            value = this.ProfilingTimer;
        end
        
        function set.TargetCollectDataFcnName(this, value)
            this.TargetCollectDataFcnName = value;
        end
        
        function value = get.TargetCollectDataFcnName(this)
            value = this.TargetCollectDataFcnName;
        end
        
        function set.SingleThreadTiming(this, value)
            this.SingleThreadTiming = value;
        end
        
        function value = get.SingleThreadTiming(this)
            value = this.SingleThreadTiming;
        end
        
        function value = get.Model(this)
            value = this.Model;
        end
        
    end
    
    methods (Access = private)
        
        function lFolderOut = stripFolderTrailingPart(~, lFolderIn)
            
            % strip trailing part like sub1/sub2/../..
            numTrailingDotDot = 0;
            
            [p,f,e] = fileparts(lFolderIn);
            
            while strcmp([f e], '..')
                lFolderIn = p;
                [p,f,e] = fileparts(lFolderIn);
                numTrailingDotDot = numTrailingDotDot + 1;
            end
            for i = 1:numTrailingDotDot
                lFolderIn = fileparts(lFolderIn);
            end
            lFolderOut = lFolderIn;
        end
        
        function setAnchorFolder(this, lAnchorFolder)
            this.AnchorFolder = this.stripFolderTrailingPart(lAnchorFolder);
        end
        
        function updateIdentifierMapping(this)
           if isempty(this.ComponentRegistries)
               % nothing to do
               return;
           end
           
           this.RegistryProbeTypesCount = cell(1, length(this.ComponentRegistries));
           this.TotalProbes = 0;
           maxProbeType = 0;
           
           this.GroupProbeTypesCount = containers.Map...
               ('KeyType', 'uint32',...
                'ValueType', 'uint64');
           
           lIdentifierMaxValue = [];
           
           % find the total number of probes
           for i = 1 : length(this.ComponentRegistries)
               
               % Check that all registries share same identifier maximum
               idMax = this.ComponentRegistries{i}.getIdentifierMax;
               if isempty(lIdentifierMaxValue)
                   lIdentifierMaxValue = idMax;
               else
                   assert(isequal(idMax, lIdentifierMaxValue), ...
                          'All registries must have the same identifier maximum');
               end
               
               this.RegistryProbeTypesCount{i} = containers.Map...
                   ('KeyType', 'uint32',...
                    'ValueType', 'uint64');
               allProbes = this.ComponentRegistries{i}.Probes;
               for j = 1 : length(allProbes)
                   probeTypeName = allProbes{j}{1};
                   probeType = this.ComponentRegistries{i}.getProbeTypeId(probeTypeName);
                   
                   if probeType > maxProbeType
                       maxProbeType = probeType;
                   end
                   
                   if ~this.RegistryProbeTypesCount{i}.isKey(uint32(probeType))
                       this.RegistryProbeTypesCount{i}(uint32(probeType)) = 0;
                   end
                   
                   if ~this.GroupProbeTypesCount.isKey(uint32(probeType))
                       this.GroupProbeTypesCount(uint32(probeType)) = 0;
                   end
                   
                   this.RegistryProbeTypesCount{i}(uint32(probeType)) =...
                       this.RegistryProbeTypesCount{i}(uint32(probeType)) + 1;
                   
                   this.GroupProbeTypesCount(uint32(probeType)) =...
                       this.GroupProbeTypesCount(uint32(probeType)) + 1;
                   
                   this.TotalProbes = this.TotalProbes + 1;
               end
           end
           
           CumulativeProbeTypesCount = containers.Map...
               ('KeyType', 'uint32',...
                'ValueType', 'uint64');
           CumulativeProbeTypesCount(0) = 0;
           for i = 1 : (maxProbeType + 1)
               totalProbesForType = 0;
               if this.GroupProbeTypesCount.isKey(i-1)
                   totalProbesForType =...
                       this.GroupProbeTypesCount(i-1);
               end
               CumulativeProbeTypesCount(i) =...
                   totalProbesForType + CumulativeProbeTypesCount(i-1);
           end
           
           if CumulativeProbeTypesCount(maxProbeType + 1) > lIdentifierMaxValue
               DAStudio.error('CoderProfile:ExecutionTime:ProfilingRegistryMaxTypeId');
           end
                                 
           this.RegistryOffsets = cell(1, length(this.ComponentRegistries));
           minMaxVals = 1;
           % find the offsets per registry per probe type
           for i = 1 : length(this.ComponentRegistries)
               this.RegistryOffsets{i} = containers.Map...
                   ('KeyType', 'uint32',...
                    'ValueType', 'uint64');
               for j = 0 : maxProbeType
                   if this.RegistryProbeTypesCount{i}.isKey{j}
                       this.RegistryOffsets{i}(j) = CumulativeProbeTypesCount(j);
                       this.RegistryValuesMin{minMaxVals} = ...
                           { CumulativeProbeTypesCount(j), ...
                             { i, j } };
                       CumulativeProbeTypesCount(j) =...
                           CumulativeProbeTypesCount(j) + this.RegistryProbeTypesCount{i}(j);
                       this.RegistryValuesMax{minMaxVals} = ...
                           { CumulativeProbeTypesCount(j), ...
                             { i, j } };
                       minMaxVals = minMaxVals + 1;
                   end
               end
           end
           
           % here we can check if cumulative count is same as the group count 
           % as a part of the safety check
       end

    end
        
    methods (Access = public)        

        
        function lAnchorFolder = getAnchorFolder(this)
            
            lAnchorFolder = this.AnchorFolder;
            
        end
        
        function updateAnchorFolder(this, lCodeFolder)
            this.setAnchorFolder(fullfile(lCodeFolder, this.RelPathToAnchor));
        end
        
        function this = ProbeGlobalRegistry(lModel, lAnchorFolder, lRelativePathToAnchor)
            this.Model = lModel;
            this.setAnchorFolder(lAnchorFolder);
            this.RelPathToAnchor = lRelativePathToAnchor;
            this.FilterProfilingOverhead = false;
            this.RunOverheadBenchmark = false;
        end
        
        function flag = areEventCallbacksRequired(this)
            % Check if there is a registry that requires the generation
            % of the event callbacks
            flag = false;
            for i=1:length(this.ComponentRegistries)
                if this.ComponentRegistries{i}.areEventCallbacksRequired
                    flag = true;
                    return;
                end
            end
        end
        
        function [globalAddressOffsets, registries] = ...
                getRegistryInfo(this)
            this.updateIdentifierMapping;
            globalAddressOffsets = this.RegistryOffsets;
            registries = this.ComponentRegistries;
        end
        
        
        function [taskSectionIds, taskPriorities] = getTaskInfo(this, probeType)
           [globalAddressOffsets, registries] = this.getRegistryInfo;
           taskSectionIds = [];
           taskPriorities = [];
           for i = 1 : length(registries)
               if registries{i}.isProbeTypePresent(coder_profile_ProbeType.TASK_TIME_PROBE)
                   taskInfo = registries{i}.getTaskInfo;
                   clear sectionIds priorities
                   [sectionIds{1:length(taskInfo)}] = deal(taskInfo(:).sectionId);
                   sectionIds = cell2mat(sectionIds);
                   sectionIds = sectionIds + globalAddressOffsets{i}(uint32(probeType));
                   taskSectionIds = [taskSectionIds(:); sectionIds(:)];
                   [priorities{1:length(taskInfo)}] = deal(taskInfo(:).priority);
                   priorities = cell2mat(priorities);
                   taskPriorities = [taskPriorities(:); priorities(:)];
               end
           end
       end
        
        
        function checksum = getFeatureChecksum(this, name)
            checksum = coder.profile.md5([]);
            for i = 1 : length(this.ComponentRegistries)
                reg = this.ComponentRegistries{i};
                checksumReg = reg.getFeatureChecksum(name);
                checksum = coder.profile.md5(checksum, checksumReg);
            end
        end
        
        function checksum = getModelInstrumentationChecksum(this)
            checksum = coder.profile.md5([]);
            
            for i = 1 : length(this.ComponentRegistries)
                reg = this.ComponentRegistries{i};
                checksum = coder.profile.md5(checksum, reg.getChecksum);
            end
        end
        
        function checksum = getChecksum(this)
            checksum = coder.profile.md5([]);
            
            for i = 1 : length(this.ComponentRegistries)
                reg = this.ComponentRegistries{i};
                checksum = coder.profile.md5(checksum, reg.getChecksum);
            end
        end
                
       function this = addRegistries(this, newRegistries)
           keepIdx = true(size(newRegistries));
           for i = 1 : length(newRegistries)
               if isempty(newRegistries{i})
                   keepIdx(i) = false;
               end
           end
           newRegistries = newRegistries(keepIdx);
           this.ComponentRegistries =...
               [this.ComponentRegistries(:); newRegistries(:)];
           
           this.updateIdentifierMapping;
       end       
              
       function writeTargetInterface(this, lTargetWordSize, lTargetBitPerChar)
           this.updateIdentifierMapping;
           allProbeTypes = keys(this.GroupProbeTypesCount);
           
           % pick any registry
           if isempty(this.ComponentRegistries)...
                   || isequal(length(allProbeTypes), 0)
               % nothing to do
               return;
           end
           aRegistry = this.ComponentRegistries{1};
           
           % create writer that will be used by the probe specific
           % target interfaces
           cFileFull = this.SourceFileTargetInterface;
           [~, f, e] = fileparts(cFileFull);
           cFileShort = [f e];
           
           hFileFull = this.HeaderFileTargetInterface;
           [~, f, e] = fileparts(hFileFull);
           hFileShort = [f e];
           
           append = false;
           % writer for writing to source file
           writerSource = rtw.connectivity.CodeWriter.create(...
               'filename', cFileFull,...
               'append', append);
           % writer for writing to header file
           writerHeader = rtw.connectivity.CodeWriter.create(...
                'filename', hFileFull,...
                'append', append);
            
           if this.RequireXCPSupport
               % writer to write the source file
               writerXCPSource = rtw.connectivity.CodeWriter.create('filename', ...
                   fullfile(this.XCPInstrumentedFolder, this.XCPMainSourceFile), ...
                   'append', append);
               % writer to write the header file
               writerXCPHeader = rtw.connectivity.CodeWriter.create('filename', ...
                   fullfile(this.XCPInstrumentedFolder, this.XCPMainHeaderFile), ...
                   'append', append);
           end
           
           % create target interfaces for all probe types that are
           % present in the registry
           interfacesWritten = {};
           pauseCallbacks = {};
           stepCallbacks = {};
           terminateCallbacks = {};
           generateCallbacks = this.areEventCallbacksRequired;
           for i = 1 : length(allProbeTypes)
               aProbeType = allProbeTypes{i};
               targetInterfaceBuilder = aRegistry.targetInterface(coder_profile_ProbeType(aProbeType));
               % we are allowing users to reuse classes that write
               % target interface but the same class will be invoked only once
               match = ...
                   sum(cellfun(@(x) isa(targetInterfaceBuilder, x),...
                            interfacesWritten));
               if ~match
                   interfacesWritten{end+1} = class(targetInterfaceBuilder); %#ok
               end
               
               targetInterfaceBuilder.init(...
                   lTargetWordSize,...
                   this.SingleThreadTiming,...
                   this.ProfilingTimer,...
                   this);
               
               % class specific init method
               targetInterfaceBuilder.targetWriteInit(match);

               % do it only once
               if isequal(i, 1)                   
                   % write the banners for source file
                   targetInterfaceBuilder.targetWriteSectionBanner...
                       (writerSource,...
                        cFileShort,...
                        false,...
                        this.ProfilingTimer);
                   
                   targetInterfaceBuilder.targetWriteSectionBanner...
                       (writerHeader,...
                        hFileShort,...
                        true,...
                        this.ProfilingTimer);
                   
                   % include the header file in the source file
                   targetInterfaceBuilder.targetWriteSectionLocalInclude...
                       (writerSource,...
                        hFileShort);
                   
                   % insert the offsets that are required to map local
                   % identifiers to the global
                   writerSource.wNewLine;
                   writerSource.wNewLine;
                   for j = 1 : length(this.ComponentRegistries)
                       allProbeCalls = this.ComponentRegistries{j}.ProbeCalls.keys;
                       comment = sprintf...
                                 ('Code instrumentation offset(s) for model %s',...
                                  this.ComponentRegistries{j}.ComponentName);
                       writerSource.wComment(comment);
                       for k = 1 : length(allProbeCalls)
                           probeTypeName = allProbeCalls{k};
                           probeType = this.ComponentRegistries{j}.getProbeTypeId(probeTypeName);
                           probeOffset = this.RegistryOffsets{j}(uint32(probeType));
                           vals = this.ComponentRegistries{j}.ProbeCalls(probeTypeName);
                           vals = vals{1};
                           for l = 1 : length(vals)
                               macroName = sprintf...
                                   ('%s_offset',...
                                    vals{l});
                               writerSource.wLine...
                                   ('#define %s %d',...
                                    macroName,...
                                    probeOffset);
                           end
                       end
                       writerSource.wNewLine;
                       writerSource.wNewLine;
                   end
                   
                   targetInterfaceBuilder.targetWriteSectionExportedIncludes...
                       (writerHeader);
                   
                   targetInterfaceBuilder.targetWriteSectionExportedFcnPrototypes...
                       (writerHeader,...
                        this.TargetCollectDataFcnName);
                    
                   targetInterfaceBuilder.targetWriteSectionExportedAtomicFcn...
                       (writerHeader,...
                        this.AtomicReadStoreFcnName);
               end
               
               % write probe specific code
               targetInterfaceBuilder.targetWriteProbeExportedFcnPrototypes(writerHeader, match);
               
               targetInterfaceBuilder.targetWriteSectionFcns...
                   (writerSource,...
                    this.TargetCollectDataFcnName,...
                    match);
                
                if generateCallbacks
                    % write pause callback
                    pauseInvocation = targetInterfaceBuilder.targetWritePauseCall;
                    if ~isempty(pauseInvocation)
                        pauseCallbacks{end+1} = pauseInvocation; %#ok
                    end
                    
                    % write terminate callback
                    terminateInvocation = targetInterfaceBuilder.targetWriteTerminateCall;
                    if ~isempty(terminateInvocation)
                        terminateCallbacks{end+1} = terminateInvocation; %#ok
                    end
                    
                    % write step callback
                    stepInvocation = targetInterfaceBuilder.targetWriteStepCall;
                    if ~isempty(stepInvocation)
                        stepCallbacks{end+1} = stepInvocation; %#ok
                    end
                end
               
               if ~match && this.RequireXCPSupport
                   % if XCP is enabled, generate the additional required
                   % code to support the feature on this simulation mode
                   % and protocol
                   targetInterfaceBuilder.targetWriteXCPInitAndValidate(this.XCPEventID);
                   targetInterfaceBuilder.targetWriteXCPManagementSource(writerXCPSource, ...
                       this.XCPCoreIDFcn, this.XCPThreadIDFcn, this.AddXCPHeaderFiles);
                   targetInterfaceBuilder.targetWriteXCPManagementHeader(writerXCPHeader);
               end
           end
           
           % code instrumentation methods per model per probe declarations
           % and their implementation
           idType = sprintf...
                    ('uint%d_T',...
                     coder.profile.ExecTimeConfig.getIdTypeSize(lTargetWordSize));
           for i = 1 : length(this.ComponentRegistries)
               reg = this.ComponentRegistries{i};
               
               % if flag is set not to generate method
               % we do not do it
               if ~reg.GenerateMethods
                   continue;
               end
               
               comment = sprintf...
                         ('Code instrumentation method(s) for model %s',...
                          reg.ComponentName);
               % write to a header
               writerHeader.wComment(comment);
               % write to a source file
               writerSource.wComment(comment);
               allProbeCalls = reg.ProbeCalls.keys;
               for j = 1 : length(allProbeCalls)
                   vals = reg.ProbeCalls(allProbeCalls{j});
                   for k = 1 : length(vals{1})
                       fncHeader = sprintf...
                           ('void %s(%s sectionId)',...
                            vals{1}{k},...
                            idType);
                       % write declaration to a header file
                       writerHeader.wLine('%s;', fncHeader);
                   
                       % write a definition to a source file
                       writerSource.wBlockStart('%s', fncHeader);
                       writerSource.wLine...
                           ('%s(%s_offset + sectionId);',...
                            vals{2}{k},...
                            vals{1}{k});
                       writerSource.wBlockEnd;
                   end
               end
               
               writerHeader.wNewLine;
               writerSource.wNewLine;
           end
           
           if generateCallbacks
               % write function containing all pause callbacks
               writerSource.writeLine('void PauseEvent (void) {');
               writerSource.incIndent;
               writerSource.wComment('callbacks executed when the sim is paused');
               for i = 1:length(pauseCallbacks)
                   writerSource.writeLine(pauseCallbacks{i});
               end
               writerSource.decIndent;
               writerSource.writeLine('}');
               
               writerHeader.wComment('Callback called when the simulation is paused');
               writerHeader.wLine('void PauseEvent (void);');
               
               % write function containing all termination callbacks
               writerSource.writeLine('void TerminateEvent (void) {');
               writerSource.incIndent;
               writerSource.wComment('callbacks executed when the sim ends');
               for i = 1:length(terminateCallbacks)
                   writerSource.writeLine(terminateCallbacks{i});
               end
               writerSource.decIndent;
               writerSource.writeLine('}');
               
               writerHeader.wComment('Callback called when the simulation ends');
               writerHeader.wLine('void TerminateEvent (void);');
               
               % write function containing all step callbacks
               writerSource.writeLine('void StepCompletedEvent (void) {');
               writerSource.incIndent;
               writerSource.wComment('callbacks executed when a step ends');
               for i = 1:length(stepCallbacks)
                   writerSource.writeLine(stepCallbacks{i});
               end
               writerSource.decIndent;
               writerSource.writeLine('}');
               
               writerHeader.wComment('Callback called when a step ends');
               writerHeader.wLine('void StepCompletedEvent (void);');
           end
           
           writerHeader.close;
           writerSource.close;          
       end
       
       function setTaskRootBlockPath(this, blockPath)
           for i = 1 : length(this.ComponentRegistries)
               reg = this.ComponentRegistries{i};
               if reg.isProbeTypePresent(coder_profile_ProbeType.TASK_TIME_PROBE)
                   reg.TaskRootBlockPath = blockPath;
               end
           end
       end
       
       function probes = getAllProbes(this)
           this.updateIdentifierMapping();
           probes = {};
           
           % check if there are registries
           if isempty(this.ComponentRegistries)
               return;
           end
           
           numRegistries = length(this.ComponentRegistries);
           
           probesNum = cell(1, numRegistries);
           for i = 1 : numRegistries
               reg = this.ComponentRegistries{i};
               probesNum{i} = reg.getProbeNumber;
           end
           
           probes = cell(1, sum([probesNum{:}]));
           totalProbes = 1;
           for i = 1 : numRegistries
               reg = this.ComponentRegistries{i};
               for j = 1 : probesNum{i}
                   probes{totalProbes} = reg.getProbe(j);
                   
                   % need to add offset to the probe id
                   probes{totalProbes}{2}{1} =...
                       probes{totalProbes}{2}{1} +...
                       this.RegistryOffsets{i}(reg.getProbeTypeId(probes{totalProbes}{1}));
                   
                   probes{totalProbes}{end+1} = reg.TaskRootBlockPath;
                   
                   totalProbes = totalProbes + 1;
               end
           end
       end
       
       function numProbes = getTotalNumberOfProbes(this)
           total = 0;
           numRegistries = length(this.ComponentRegistries);
           for i = 1 : numRegistries
               total = total + this.ComponentRegistries{i}.getProbeNumber;
           end
           
           numProbes = total;
       end
       
       function numProbes = getTotalNumberOfTypeProbes(this, lProbeType)
           numProbes = 0;
           
           numRegistries = length(this.ComponentRegistries);
           for i = 1 : numRegistries
               allProbes = this.ComponentRegistries{i}.Probes;
               for j = 1 : length(allProbes)
                   pType = this.ComponentRegistries{i}.getProbeTypeId(allProbes{j}{1});
                   if isequal(pType, lProbeType)
                       numProbes = numProbes + 1;
                   end
               end
           end
           
       end
       
       function updateTraceInfo(this, lInstrCodeSubFolder)
           for i = 1 : length(this.ComponentRegistries)
               this.ComponentRegistries{i}.updateTraceInfo...
                   (this.getAnchorFolder, lInstrCodeSubFolder);
           end
       end
       
       function lSectionTraceInfoCA = getInfoForSectionId(this, lSectionIdsGlobal)
           this.updateIdentifierMapping;
           
           lSectionTraceInfoCA = cell(size(lSectionIdsGlobal));
           
           regIdx = zeros(size(lSectionIdsGlobal));
           
           for ii = 1 : length(this.RegistryValuesMin)
               regIdxMatch = ...
                   lSectionIdsGlobal > this.RegistryValuesMin{ii}{1} & ...
                   lSectionIdsGlobal <= this.RegistryValuesMax{ii}{1};
               regIdx(regIdxMatch) = ii;
           end
           
           for ii = 1 : length(lSectionIdsGlobal)
               idx = regIdx(ii);
               
               lSectionIdLocal =...
                   lSectionIdsGlobal(ii) - this.RegistryValuesMin{idx}{1};
               lRegistry = this.ComponentRegistries...
                   {this.RegistryValuesMin{idx}{2}{1}};
               lProbeType = this.RegistryValuesMin{idx}{2}{2};
               
               lSectionTraceInfoCA{ii} = ...
                   lRegistry.getSectionTraceInfo(...
                       lSectionIdLocal, lProbeType, this.getAnchorFolder);
           end
       end
       
       function isValid = isTraceabilityValid(this)
           isValid = true;
           
           for i = 1 : length(this.ComponentRegistries)
               % if we are not generating methods for the registry
               % then also we do not need to check if traceability is valid
               if ~this.ComponentRegistries{i}.GenerateMethods
                   continue;
               end
               
               storedChecksum = this.ComponentRegistries{i}.getStoredTraceabilityChecksum;
               lChecksum = this.ComponentRegistries{i}...
                   .getTraceabilityChecksum(this.getAnchorFolder);
               if isempty(storedChecksum) || isempty(lChecksum)...
                       || ~strcmp(lChecksum, storedChecksum)
                   isValid = false;
                   break;
               end
           end
       end
              
       function res = getTimeProbeRangeIds(this)
           this.updateIdentifierMapping;
           % this method returns a list of information for each group of
           % registered TIME probes. Note, non-time probes are not included
           % Each tuple contains the number of probes and the offset (due
           % to previous registries)
           res = [];
           for i=1:length(this.ComponentRegistries)
               reg = this.ComponentRegistries{i};
               offsets = this.RegistryOffsets{i};
               keys = offsets.keys;
               for j=1:offsets.Count
                   key = keys{j};
                   if key == coder_profile_ProbeType.EXEC_TIME_PROBE || key == coder_profile_ProbeType.TASK_TIME_PROBE
                       [lmin, lmax] = reg.getSectionIdRange(key);
                       if lmin > 0 && lmax > 0
                           res = [res; lmax, offsets(key)]; %#ok<AGROW>
                       end
                   end
               end
           end
           % The map may contain consecutive tuples with consecutive
           % indexes. For example, [3 4; 2 7] means that there are 3 time
           % probes with offset 4 and then 2 time probes with offset 7.
           % Since 4+3 is 7, the IDs for the two tuples are consecutive.
           % In this case, we can merge the tuples and have only [5 4]
           % The advantage is that the generated number of checks with
           % capture probes is lower
           toRestart = true;
           while toRestart
               toRestart = false;
               numRows = size(res,1);
               for i=2:numRows
                   if (res(i-1, 1) + res(i-1, 2)) == res(i, 2)
                       res(i-1, 1) = res(i-1, 1) + res(i, 1); %#ok<AGROW>
                       res(i, :) = []; %#ok<AGROW>
                       toRestart = true;
                       break;
                   end
               end
           end
       end

       function updateNumberOfTimeProbes(this)
           this.TotalTimeProbe = ...
               this.getTotalNumberOfTypeProbes(coder_profile_ProbeType.EXEC_TIME_PROBE) + ...
               this.getTotalNumberOfTypeProbes(coder_profile_ProbeType.TASK_TIME_PROBE);
       end
              
    end
    
end

% LocalWords:  XCP xcp
