classdef (Hidden = true, Abstract) ProbeComponentRegistry < coder_profile_RuntimeMetricsRegistry
%

% Copyright 2013-2023 The MathWorks, Inc.
properties (Access = private)
CodeFolderRelative;
Checksum;

AllTraceInfo;

ProfSecsTraceInfo;

% Call site for each stored probe. Same order as in SectionIds
ProfSecsCallSiteNames;
% Function name for each stored probe. Same order as in SectionIds
ProfSecsCodeName;
% SID (model block) for each stored probe. Same order as in SectionIds
ProfSecsCallSiteSids;
% Sample time for each stored probe. Same order as in SectionIds
ProfSecsSampleTime;

% For each stored probe type (using its name as key) all the relative
% section IDs of the registered probes
SectionIds;

StoredTraceabilityChecksum;

% Generic container to store checksum information
FeatureChecksumContainer;

IdentifierMax;

BuildInfoFileName = 'buildInfo.mat';
end

properties (Access = protected)
% This data structure contains the profiled lines in files
ProfSecsSourceFile;

% Maximum length for the instrumentation API
MaxIdLength;
end

properties (Access = public)
% the value of this map is 1x2 cell array where the first member is
% wrapper method that maps local to global ids and the section
% member is actual instrumentation method
ProbeCalls;
ComponentName;
ComponentRef;

TaskRootBlockPath;

GenerateMethods;
end

properties (Access = public, Hidden)
% if true, generate C wrapper for the instrumentation API
NeedAPIWrapper = false;
% state if the generated instrumentation API is compiled as C code
IsInstrumentationCAPI = false;
end

% get and set methods
methods
function value = get.ComponentName(this)
value = this.ComponentName;
end

function value = get.ProbeCalls(this)
value = this.ProbeCalls;
end

function value = get.ComponentRef(this)
value = this.ComponentRef;
end

function set.TaskRootBlockPath(this, value)
this.TaskRootBlockPath = value;
end

function value = get.TaskRootBlockPath(this)
value = this.TaskRootBlockPath;
end
end

methods (Access = protected)

function probeFcnCall = addProbeIdentifier...
(this, lProbeType, lProbeTypeName, lSectionId, lTraceInfo)
this.addInternalSectionId(lProbeTypeName, lSectionId);

this.checkMaxIdLimit(lProbeTypeName);

this.updateProfilingSections(lProbeTypeName, lTraceInfo);

probeFcnCall = this.instrumentationFnc(lProbeType, lSectionId);

end

function addInternalSectionId (this, lProbeTypeName, lSectionId)
if ~this.SectionIds.isKey(lProbeTypeName)
this.SectionIds(lProbeTypeName) = uint64([]);
end

mSectionIds = this.SectionIds(lProbeTypeName);
mSectionIds(end+1) = lSectionId;
this.SectionIds(lProbeTypeName) = mSectionIds;
end

function checkMaxIdLimit(this, lProbeTypeName)
numKeys = 0;
if this.SectionIds.isKey(lProbeTypeName)
numKeys = length(this.SectionIds(lProbeTypeName));
end

if numKeys > this.getIdentifierMax
DAStudio.error(...
'CoderProfile:ExecutionTime:ProfilingRegistryMaxTypeId');
end
end

function updateProfilingSections(this, lProbeTypeName, lModelTraceInfo)
% This function adds all additional information related to a
% probe, such as function name, SID and sample time
% Probes are sorted as in SectionIds

% code name
if ~this.ProfSecsCodeName.isKey(lProbeTypeName)
this.ProfSecsCodeName(lProbeTypeName) = {};
end
lProfSecsCodeName = this.ProfSecsCodeName(lProbeTypeName);
lProfSecsCodeName{end+1} = lModelTraceInfo.CodeName;
this.ProfSecsCodeName(lProbeTypeName) = lProfSecsCodeName;

% call site SIDS
if ~this.ProfSecsCallSiteSids.isKey(lProbeTypeName)
this.ProfSecsCallSiteSids(lProbeTypeName) = {};
end
lProfSecsCallSiteSids = this.ProfSecsCallSiteSids(lProbeTypeName);
lProfSecsCallSiteSids{end+1} = lModelTraceInfo.CallSiteSids;
this.ProfSecsCallSiteSids(lProbeTypeName) = lProfSecsCallSiteSids;

% call site names
if ~this.ProfSecsCallSiteNames.isKey(lProbeTypeName)
this.ProfSecsCallSiteNames(lProbeTypeName) = {};
end
lProfSecsCallSiteNames = this.ProfSecsCallSiteNames(lProbeTypeName);
lProfSecsCallSiteNames{end+1} = lModelTraceInfo.CallSiteNames;
this.ProfSecsCallSiteNames(lProbeTypeName) = lProfSecsCallSiteNames;

% sample time
if ~this.ProfSecsSampleTime.isKey(lProbeTypeName)
this.ProfSecsSampleTime(lProbeTypeName) = {};
end
lProfSecsSampleTime = this.ProfSecsSampleTime(lProbeTypeName);
if isfield(lModelTraceInfo, 'ProfSecsSampleTime')
lProfSecsSampleTime{end+1} = lModelTraceInfo.ProfSecsSampleTime;
else
lProfSecsSampleTime{end+1} = [];
end
this.ProfSecsSampleTime(lProbeTypeName) = lProfSecsSampleTime;

this.Checksum = coder.profile.md5(this.Checksum, ...
[cell2mat(lModelTraceInfo.CallSiteSids) lModelTraceInfo.CodeName]);
end

end

methods (Access = private)

function out = getCodeIdentifiersInternal(this, lProbeTypeName)
% the probe type must exist
assert(this.ProbeCalls.isKey(lProbeTypeName),...
'Probe type does not exist in the ProbeCalls container');
vals = this.ProbeCalls(lProbeTypeName);

% the identifiers for the probe that were put in
% the code are in the first cell array
vals = vals{1};

startFcnName = vals{1};
startMacroName = vals{1};
if(length(vals) == 2)
endFcnName = vals{2};
endMacroName = vals{2};
else
endFcnName = '';
endMacroName = '';
end

% return struct
out = struct(...
'startFcnName', {startFcnName},...
'endFcnName', {endFcnName},...
'startMacroName', {startMacroName},...
'endMacroName', {endMacroName});
end

function macroName = getMacroName(~, str)
macroName = sprintf('MCR_%s', upper(str));
end

function lBuildDir = profGetBuildDir(this, lAnchorFolder)
if isempty(this.CodeFolderRelative)
lBuildDir = '';
else
lBuildDir = fullfile(lAnchorFolder, this.CodeFolderRelative);
end
end

function [lineNos, sectionIds, fileNotFound] = getProfiledLinesFromSrcFile...
(~, srcFile, profilingMacroIdentifiers)

if exist(srcFile, 'file')
fileContent = fileread(srcFile);
fileNotFound = false;
else
fileContent = '';
fileNotFound = true;
end

expr = '\n';
newLineIdx = regexp(fileContent, expr);

allToks = {};
allStartIdx = [];
exprOrigLine = '\s*(/\* original_line:([0-9]+) \*/)?';
for i=1:length(profilingMacroIdentifiers)
identifier = profilingMacroIdentifiers{i};
expr = [ identifier '\s*\(\s*([0-9]*)U+\s*\);' exprOrigLine ];
[toks, startIdx] = regexp(fileContent, expr, 'tokens');
allToks = [allToks(:) toks(:)];
allStartIdx = [allStartIdx; startIdx(:)]; %#ok
end

lineNos = zeros(size(allStartIdx));
sectionIds = zeros(size(allStartIdx));

for i=1:length(lineNos)
sectionIds(i)=str2double(allToks{i}{1});
startIdx = allStartIdx(i);
originalLineStr = allToks{i}{2};
% The special original_line comment, if present, indicates the line number in
% the original source file
if ~isempty(originalLineStr)
originalLine = str2double...
(regexp(originalLineStr, '[0-9]+', 'match', 'once'));
lineNos(i) = originalLine;
else
% No original line number comment was found: this occurs for test harness files
% where the instrumentation is inserted directly in the
% original source file.
lineNos(i) = find(newLineIdx>startIdx, 1 );
end
end

end

function [traceSectionIds, traceSrcFileIdx, traceLineNumbers] = ...
updateSourceFileTraceInfo...
(this, lAnchorFolder, lInstrCodeSubFolder)

traceSectionIds = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');
traceSrcFileIdx = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');
traceLineNumbers = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');

allFileNames = {};
allPaths = {};

buildDir = this.profGetBuildDir(lAnchorFolder);

% Initialize the traceability checksum
this.StoredTraceabilityChecksum = this.getTraceabilityChecksum...
(lAnchorFolder);

if isempty(buildDir)
srcFileInfo = [];
else
try
reportInfo = rtw.report.getReportInfo('',buildDir);
srcFileInfo = reportInfo.getFileInfo;
catch e
if any(strcmp(e.identifier, ...
{'RTW:report:invalidBuildFolder',...
'RTW:report:buildFolderNotFound'}))
% reportInfo is unavailable. The build folder might have
% been removed or might be out of date. We can still
% generate the report. There will
% be a graceful error message on clicking a hyperlink to
% code from inside the report.
srcFileInfo = [];
elseif strcmp(e.identifier, 'RTW:report:ReportInfoNotFound')
% reportInfo is unavailable. This happens for
% shared utilities as that information is missing for
% them
srcFileInfo = [];
else
rethrow(e)
end
end
end

if ~isempty(srcFileInfo)
% we need to go through each probe
allProbeTypes = this.ProbeCalls.keys;
for j = 1 : length(allProbeTypes)
lFileNames = cell(size(srcFileInfo));
lPaths = cell(size(srcFileInfo));
allLineNos = [];
allSectionIds = [];
allSrcFileIdx = [];

out = this.getCodeIdentifiersInternal(allProbeTypes{j});
if ~isequal(out.endMacroName, 0)
profilingMacroIdentifiers = ...
{out.startMacroName, out.endMacroName};
else
profilingMacroIdentifiers = {out.startMacroName};
end

allSrcFilesFound = true;
for i=1:length(lFileNames)
lPath=srcFileInfo(i).Path;
lFileName = srcFileInfo(i).FileName;
srcFile=fullfile(lPath, lFileName);

if ~isempty(lInstrCodeSubFolder)
% We are using a sub-folder to hold an instrumented copy
% of the original source file
lPathInstr = fullfile(lPath, lInstrCodeSubFolder);
srcFileSubFolder = fullfile(lPathInstr, lFileName);
if ~exist(srcFileSubFolder, 'file')
% For certain files (e.g. test harness files) the instrumentation
% is inserted directly in the original file
srcFileInstr = srcFile;
else
srcFileInstr = srcFileSubFolder;
end
else
% No separate sub-folder for instrumented copy of the original
% code
srcFileInstr = srcFile;
end

% check to see if we already have line numbers
% for this file in the internal data structure
if this.ProfSecsSourceFile.isKey(lFileName)
fileNotFound = false;
entries = this.ProfSecsSourceFile(lFileName);
total = length(profilingMacroIdentifiers) * length(entries);
tmpLineNos = zeros(total, 1);
tmpSectionIds = zeros(total, 1);
for ii = 1 : length(profilingMacroIdentifiers)
for jj = 1 : length(entries)
idx = (ii - 1)*length(entries) + jj;
tmpSectionIds(idx) = double(entries{jj}{1});
tmpLineNos(idx) = double(entries{jj}{2}{ii});
end
end
else
[tmpLineNos, tmpSectionIds, fileNotFound] = ...
this.getProfiledLinesFromSrcFile(...
srcFileInstr, ...
profilingMacroIdentifiers);
end

if fileNotFound
allSrcFilesFound = false;
end

lPaths{i}=lPath;
lFileNames{i}=lFileName;
tmpSrcFileIdx = repmat(i, length(tmpLineNos), 1);
allLineNos = [allLineNos; tmpLineNos]; %#ok
allSectionIds = [allSectionIds; tmpSectionIds]; %#ok
allSrcFileIdx = [allSrcFileIdx; tmpSrcFileIdx]; %#ok
end

if ~allSrcFilesFound
MSLDiagnostic...
('CoderProfile:ExecutionTime:ProfilingBuildFolderInconsistent').reportAsWarning;
end

allPaths(end+1:end+length(lPaths)) = lPaths(:);

numFileNamesAlreadyAdded = length(allFileNames);
allFileNames(end+1:end+length(lFileNames)) = lFileNames(:);
traceSectionIds(allProbeTypes{j}) = allSectionIds;
traceSrcFileIdx(allProbeTypes{j}) = ...
allSrcFileIdx + numFileNamesAlreadyAdded;
traceLineNumbers(allProbeTypes{j}) = allLineNos;
end
else
lBuildInfoFile = fullfile(lAnchorFolder, this.BuildInfoFileName);
if isfile(lBuildInfoFile)
lBuildInfo = load(lBuildInfoFile);
lFiles = lBuildInfo.buildInfo.Src.Files();
numCodeGenFiles = length(lFiles);
% get list of generated files
lCodeGenFiles = cell(size(srcFileInfo));
for i = 1:numCodeGenFiles
lCodeGenFiles{i} = lFiles(i).FileName;
end
keys = this.ProfSecsSourceFile.keys;
allFileNames = cell(1, length(keys));
allPaths = cell(1, length(keys));

allProbeTypes = this.ProbeCalls.keys;
% filter out offset probe which are used internally
% only
allProbeTypes(strcmp(allProbeTypes, 'OFFSET_STACK_PROBE')) = [];
allProbeTypes(strcmp(allProbeTypes, 'CATCH_STACK_PROBE')) = [];
assert(length(allProbeTypes) < 2);
if length(allProbeTypes) == 1
probeType = allProbeTypes{1};
allLineNos = [];
allSectionIds = [];
allSrcFileIdx = [];

out = this.getCodeIdentifiersInternal(probeType);
if ~isequal(out.endMacroName, 0)
profilingMacroIdentifiers = ...
{out.startMacroName, out.endMacroName};
else
profilingMacroIdentifiers = {out.startMacroName};
end
% process files with instrumentation
keys = this.ProfSecsSourceFile.keys;
for i=1:length(keys)
key = keys{i};
[lPath, lFile, lExt] = fileparts(key);
lFileName = [lFile, lExt];
idx = find(strcmp(lFileName, lCodeGenFiles));
if ~isempty(idx)
% this is either a generated file or custom source file
allFileNames{i} = lFiles(idx).FileName;
allPaths{i} = strrep(lFiles(idx).Path, '$(START_DIR)', ...
lBuildInfo.buildInfo.Settings.LocalAnchorDir);
else
% this is a custom header file containing instrumented functions
allFileNames{i} = lFileName;
allPaths{i} = lPath;
end

entries = this.ProfSecsSourceFile(key);
total = length(profilingMacroIdentifiers) * length(entries);
tmpLineNos = zeros(total, 1);
tmpSectionIds = zeros(total, 1);
for ii = 1 : length(profilingMacroIdentifiers)
for jj = 1 : length(entries)
idx = (ii - 1)*length(entries) + jj;
tmpSectionIds(idx) = double(entries{jj}{1});
tmpLineNos(idx) = double(entries{jj}{2}{ii});
end
end

tmpSrcFileIdx = repmat(i, length(tmpLineNos), 1);
allLineNos = [allLineNos; tmpLineNos]; %#ok<AGROW>
allSectionIds = [allSectionIds; tmpSectionIds]; %#ok<AGROW>
allSrcFileIdx = [allSrcFileIdx; tmpSrcFileIdx]; %#ok<AGROW>
end
traceSectionIds(probeType) = allSectionIds;
traceSrcFileIdx(probeType) = allSrcFileIdx;
traceLineNumbers(probeType) = allLineNos;
end
end
end
this.AllTraceInfo = struct('SourceFileNames', {allFileNames}, ...
'SourceFilePaths', {allPaths});
end

function updateSectionTraceInfo(this, traceSectionIds, traceSrcFileIdx, traceLineNumbers)

lProbeTypeNames = this.SectionIds.keys;
for i = 1 : length(lProbeTypeNames)
pSectionIds = this.SectionIds(lProbeTypeNames{i});
if ~this.ProfSecsTraceInfo.isKey(lProbeTypeNames{i})
this.ProfSecsTraceInfo(lProbeTypeNames{i}) =...
struct(...
'SrcFileIdx', {},...
'LineNumbersIdx', {});
end
pProfSecsTraceInfo = this.ProfSecsTraceInfo(lProbeTypeNames{i});

for j = 1 : length(pSectionIds)
lSectionId = pSectionIds(j);

lSrcFileIdx = [];
lLineNumbersIdx = [];

if traceSectionIds.isKey(lProbeTypeNames{i}) &&...
~isempty(traceSectionIds(lProbeTypeNames{i}))
matchIdx = (traceSectionIds(lProbeTypeNames{i}) == lSectionId);

if traceSrcFileIdx.isKey(lProbeTypeNames{i})
mTraceSrcFileIdx = traceSrcFileIdx(lProbeTypeNames{i});
lSrcFileIdx = mTraceSrcFileIdx(matchIdx);
end

if traceLineNumbers.isKey(lProbeTypeNames{i})
mTraceLineNumbers = traceLineNumbers(lProbeTypeNames{i});
lLineNumbersIdx = mTraceLineNumbers(matchIdx);
end

end

pProfSecsTraceInfo(j).SrcFileIdx = lSrcFileIdx;
pProfSecsTraceInfo(j).LineNumbersIdx = lLineNumbersIdx;
end

this.ProfSecsTraceInfo(lProbeTypeNames{i}) = pProfSecsTraceInfo;
end

end

function [charPos, pCall] = findPlaceToInsertProbeCall(...
~,...
oldCharPos,...
lineNum,...
lines,...
start,...
fcnCall,...
sectionId)
charPos = oldCharPos;
pCall = sprintf('%s(%dU); /* original_line:%d */', fcnCall, sectionId, lineNum);

line = lines{lineNum};
% check to see if line starts with preprocessor directive
if ~isempty(regexp(strtrim(line), '^#', 'ONCE'))
% check to see if char position is after directive
lineStartCharPos =...
sum(cellfun(@length, lines(1:(lineNum-1)))) + lineNum;
lineEndCharPos = lineStartCharPos + length(line);
% check to see if we want to insert probe on the same line as directive
assert(oldCharPos >= lineStartCharPos && oldCharPos <= lineEndCharPos, ...
'The probe insertion point should be on the directive line');
% if it is start probe we should insert before
% directive on new line, while if it is end probe
% then we should after directive on new line
if start
charPos = lineStartCharPos - 1;
pCall = sprintf('\n%s', pCall);
else
charPos = lineEndCharPos + 1;
pCall = sprintf('%s\n', pCall);
end
end
end
end

methods (Access = public)

function setFeatureChecksum (this, name, value)
this.FeatureChecksumContainer(name) = value;
end

function checksum = getFeatureChecksum (this, name)
if this.FeatureChecksumContainer.isKey(name)
checksum = this.FeatureChecksumContainer(name);
else
checksum = coder.profile.md5([]);
end
end

function [lmin, lmax] = getSectionIdRange(this, probeTypeId)
key = coder_profile_ProbeType.toString(probeTypeId);
if this.SectionIds.isKey(key)
values = this.SectionIds(key);
lmin = min(values);
lmax = max(values);
else
lmin = 0;
lmax = 0;
end
end

% this method states if this registry needs the generation of the
% event callbacks in xil_interface.c and xil_instrumentation.c:
% PauseEvent, StepCompletedEvent and TerminateEvent. Such callbacks
% are generated only on demand.
% The default implementation returns false
function eventCallbacksRequried = areEventCallbacksRequired(~)
eventCallbacksRequried = false;
end

% this method makes the registry check if the probes are correctly
% registered in the infrastructure. If not, registries should load
% them.
% The registries that use probes that are dynamic registered must
% override this method
function checkRegisteredProbes(~)
end

function flag = isProbeTypePresent(this, probeType)
lProbeTypeName = coder_profile_ProbeType.toString(uint32(probeType));
flag = this.SectionIds.isKey(lProbeTypeName);
end

function sectionIds = getSectionIds(this, lProbeType)
sectionIds = [];
k = coder_profile_ProbeType.toString(uint32(lProbeType));

if this.SectionIds.isKey(k)
sectionIds = this.SectionIds(k);
end
end

function probeTypes = getAllProbeTypes(this)
keys = this.SectionIds.keys;
probeTypes = cell(1, length(keys));
for i = 1:length(keys)
if ~iscellstr(keys(i)) %#ok<ISCLSTR>
DAStudio.error('CoderProfile:ExecutionTime:InvalidProbesDataFormat');
end
probeTypes{i} = uint32(coder_profile_ProbeType(keys(i)));
end
end

function lIdentifierMax = getIdentifierMax(this)
lIdentifierMax = this.IdentifierMax;
end

function checksum = getTraceabilityChecksum(this, lAnchorFolder)
checksum = coder.profile.ProbeComponentRegistry...
.getTraceabilityChecksumForBuildDir...
(this.profGetBuildDir(lAnchorFolder));
end

function chksum = getStoredTraceabilityChecksum(this)
chksum = this.StoredTraceabilityChecksum;
end

function probeFncCall = instrumentationFnc(this, lProbeType, lSectionId)
lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
probeRealInstrFncs =...
this.getProbeInstrumentationFcn(...
lProbeType,...
lSectionId);

probeInstrFncs =...
arrayfun(@(x) strcat(x, '_', this.ComponentName),...
probeRealInstrFncs);

probeFncCall = ...
arrayfun(@(x) coder.profile.private.createProbeIdentifier...
(x.name, this.MaxIdLength),...
cell2struct(probeInstrFncs, {'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}, probeFncCall),...
'The wrapper probe calls should be same across the same probe type');
assert(isequal(vals{2}, probeRealInstrFncs),...
'The real instrumentation probe calls should be same across the same probe type');
else
vals = cell(1,2);
vals{1} = probeFncCall;
vals{2} = probeRealInstrFncs;
this.ProbeCalls(lProbeTypeName) = vals;
end
end

function this = ProbeComponentRegistry...
(lComponentName,...
lComponentRef,...
lTargetWordSize,...
~,...
lMaxIdLength,...
lCodeFolderRelative)

if nargin < 6
lCodeFolderRelative = '';
end

this.CodeFolderRelative = lCodeFolderRelative;

this.ComponentName = lComponentName;

this.MaxIdLength = lMaxIdLength;
% need to check if this component reference already
% exists, if it exists need to somehow fail gracefully
this.ComponentRef = lComponentRef;

checksum = coder.profile.md5([]);
this.Checksum = coder.profile.md5(checksum, lComponentName);

this.FeatureChecksumContainer = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');

this.IdentifierMax = coder.profile.ExecTimeConfig...
.getIdentifierMaxValueFromTargetWordSize(lTargetWordSize);

% create map from a probe type to the actual
% method that is invoked

% the value of this map is 1x2 cell array where
% the first member is wrapper method that maps local
% to global ids and the second member is actual
% instrumentation method
this.ProbeCalls = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');

% initialize the rest of containers
this.ProfSecsTraceInfo = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');

this.ProfSecsSourceFile = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');
this.ProfSecsCodeName = containers.Map...
('KeyType', 'char', ...
'ValueType', 'any');
this.ProfSecsCallSiteSids = containers.Map...
('KeyType', 'char', ...
'ValueType', 'any');
this.ProfSecsCallSiteNames = containers.Map...
('KeyType', 'char', ...
'ValueType', 'any');
this.ProfSecsSampleTime = containers.Map...
('KeyType', 'char', ...
'ValueType', 'any');

this.SectionIds = containers.Map...
('KeyType', 'char',...
'ValueType', 'any');

this.GenerateMethods = true;

end

function checksum = getChecksum(this)
checksum = this.Checksum;
end

function insertProbes(this, profTraceInfo, lDeclarationSite,...
lInstrSrcFolder, targetWordSize, codeDescriptor)
% check to see if we have EndFcnCharNo, if we do then
% we need to add probe at the start and at the end

arguments
this
profTraceInfo
lDeclarationSite
lInstrSrcFolder = ''
targetWordSize = []
codeDescriptor = []
end

endProbesNumber = sum...
(arrayfun(@(x) ~isempty(x.EndFcnCharNo),...
profTraceInfo.ProbeSites));

numCodeInserts = length(profTraceInfo.ProbeSites) + endProbesNumber;
probeCode = cell(1, numCodeInserts);
probeFileIdx = zeros(1, numCodeInserts);
probeStartPos = zeros(1, numCodeInserts);

assert(length(profTraceInfo.ProbeSites) == length(profTraceInfo.ProbeTypes),...
'Number of probe sites and number of probe types must be equal');

lFileNames = profTraceInfo.FileNames;
lFileIdxs = unique([profTraceInfo.ProbeSites.FileNameIdx]);
lFileName = cell(1, length(lFileIdxs));
contents = cell(1, length(lFileIdxs));
contentPerLine = cell(1, length(lFileIdxs));

for i = 1 : length(lFileNames)
if isempty(find(lFileIdxs == i, 1))
continue;
end
lFileName{i} = lFileNames{i};
if ~isempty(lInstrSrcFolder)
[p,f,e] = fileparts(lFileName{i});
lFileName{i} = fullfile(p, lInstrSrcFolder, [f e]);
end
contents{i} = fileread(lFileName{i});
contentPerLine{i} = strsplit(...
contents{i},...
'\n',...
'CollapseDelimiters',...
false);
end

probesCount = 1;
for i = 1 : length(profTraceInfo.ProbeSites)
lTraceId = profTraceInfo.ProbeSites(i).TraceId;
lProbeType = profTraceInfo.ProbeTypes{i};
lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
lSectionId = this.requestSectionId...
(lProbeType,...
profTraceInfo.ModelTraceInfo(lTraceId).CodeName);
probeFncCall = this.addProbeIdentifier...
(lProbeType, lProbeTypeName, lSectionId, profTraceInfo.ModelTraceInfo(lTraceId));
probeFileIdx(probesCount) = profTraceInfo.ProbeSites(i).FileNameIdx;
[probeStartPos(probesCount), probeCode{probesCount}] =...
this.findPlaceToInsertProbeCall(...
profTraceInfo.ProbeSites(i).StartFcnCharNo(1),...
profTraceInfo.ProbeSites(i).StartLineNo,...
contentPerLine{profTraceInfo.ProbeSites(i).FileNameIdx},...
true,...
probeFncCall{1},...
lSectionId);
probesCount = probesCount + 1;
if isequal(length(probeFncCall), 2)
% if there are two methods then EndFcnCharNo should not be empty
assert(~isempty(profTraceInfo.ProbeSites(i).EndFcnCharNo(1)),...
'No end point for the probe');
probeFileIdx(probesCount) = profTraceInfo.ProbeSites(i).FileNameIdx;
[probeStartPos(probesCount), probeCode{probesCount}] =...
this.findPlaceToInsertProbeCall(...
profTraceInfo.ProbeSites(i).EndFcnCharNo(1),...
profTraceInfo.ProbeSites(i).EndLineNo,...
contentPerLine{profTraceInfo.ProbeSites(i).FileNameIdx},...
false,...
probeFncCall{2},...
lSectionId);
probesCount = probesCount + 1;
end
end

for i = 1 : length(lFileNames)
idx = probeFileIdx == i;
startPosns = probeStartPos(idx);
if ~isempty(startPosns)
codePieces = probeCode(idx);
[~, startPosnsSortIdx] = sort(startPosns);
% Insert probes starting from the end of the file
insertOrder = flipud(startPosnsSortIdx(:));

content = contents{i};
for ii1 = insertOrder'
content = sprintf('%s%s%s',...
content(1:startPosns(ii1)-1),...
codePieces{ii1},...
content(startPosns(ii1):end));
end
fid = fopen(lFileName{i}, 'w');
fwrite(fid, content);
fclose(fid);
end
end

% insert extern declarations to header file
if this.ProbeCalls.Count > 0

% Get type name for probe identifier
idTypeName = coder.profile.ExecTimeConfig.getIdentifierCDataType...
(targetWordSize, codeDescriptor);

decls = '';
decls = sprintf('%s\n\n/* Declare global externs for instrumentation */', decls);

for key = this.ProbeCalls.keys
values = this.ProbeCalls(key{1});
values = values{1};
for i = 1 : length(values)
decls = sprintf('%s\nextern void %s(%s);', decls, values{i}, idTypeName);
end
end

decls = sprintf('%s\n', decls);

hdrFile = lDeclarationSite.FileName;
if ~isempty(lInstrSrcFolder)
[p,f,e] = fileparts(hdrFile);
hdrFile = fullfile(p, lInstrSrcFolder, [f e]);
end
content = fileread(hdrFile);
insCharNo = lDeclarationSite.CharNo;
content = [content(1:insCharNo-1) decls content(insCharNo:end)];

fid = fopen(hdrFile, 'w');
fwrite(fid, content);
fclose(fid);
end
end

function out = getCodeIdentifiers(this, probeType)
lProbeTypeName = coder_profile_ProbeType.toString(uint32(probeType));

if this.ProbeCalls.isKey(lProbeTypeName)
out = this.getCodeIdentifiersInternal(lProbeTypeName);
else
out = [];
end
end

function updateTraceInfo(this, lAnchorFolder, lInstrCodeSubFolder)
% Parse all source files to extract line number information
[traceSectionIds, traceSrcFileIdx, traceLineNumbers] = ...
this.updateSourceFileTraceInfo...
(lAnchorFolder, lInstrCodeSubFolder);

% Associate line number information with individual sections
this.updateSectionTraceInfo(traceSectionIds, ...
traceSrcFileIdx, ...
traceLineNumbers);
end

function lSectionTraceInfo = getSectionTraceInfo...
(this, lSectionId, lProbeType, lAnchorFolder)
lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
lFileNames = {};
lPaths = {};
lSrcFileIdx = [];
lLineNumbersIdx = [];

supportedProbeTypes = [coder_profile_ProbeType.TASK_TIME_PROBE, ...
coder_profile_ProbeType.EXEC_TIME_PROBE, ...
coder_profile_ProbeType.TASK_STACK_PROBE, ...
coder_profile_ProbeType.EXEC_STACK_PROBE];
if ~ismember(lProbeType, supportedProbeTypes)
lSectionTraceInfo = coder.profile.SectionTraceInfo.empty;
return;
end

lIsTask = false;
taskProbeTypes = [coder_profile_ProbeType.TASK_TIME_PROBE, ...
coder_profile_ProbeType.TASK_STACK_PROBE];
if ismember(lProbeType, taskProbeTypes)
lIsTask = true;
end

assert(this.SectionIds.isKey(lProbeTypeName),...
'The probe type does not exists in the registry');
pSectionIds = this.SectionIds(lProbeTypeName);
idx = find(pSectionIds == lSectionId, 1);
assert(~isempty(idx), ...
'The probe identification tag cannot be found in the registry');

pProfSecsCodeName = this.ProfSecsCodeName(lProbeTypeName);
lProfiledCodeName = pProfSecsCodeName{idx};
pCallSiteNames = this.ProfSecsCallSiteNames(lProbeTypeName);
lCallSiteNames = pCallSiteNames{idx};
pCallSiteSids = this.ProfSecsCallSiteSids(lProbeTypeName);
lCallSiteSids = pCallSiteSids{idx};
pSampleTime = this.ProfSecsSampleTime(lProbeTypeName);
sampleTime = pSampleTime{idx};

if ~this.ProfSecsTraceInfo.isKey(lProbeTypeName)
pProfSecsTraceInfo = [];
else
pProfSecsTraceInfo = this.ProfSecsTraceInfo(lProbeTypeName);
end
if ~isempty(pProfSecsTraceInfo)
lSrcFileIdx = pProfSecsTraceInfo(idx).SrcFileIdx;
lLineNumbersIdx = pProfSecsTraceInfo(idx).LineNumbersIdx;
end

if ~isempty(lSrcFileIdx)
% Remove unreferenced files and remap the index
usedFileIdx = unique(lSrcFileIdx);
lFileNames = this.AllTraceInfo.SourceFileNames(usedFileIdx);
lPaths = this.AllTraceInfo.SourceFilePaths(usedFileIdx);
for i = 1 : length(usedFileIdx)
idx1 = lSrcFileIdx == usedFileIdx(i);
lSrcFileIdx(idx1) = i; %#ok
end
end

lSectionTraceInfo = coder.profile.SectionTraceInfo...
(lFileNames, lPaths, lSrcFileIdx, ...
lLineNumbersIdx, lProfiledCodeName,...
[], [], ...
lCallSiteNames, lCallSiteSids, ...
this.getStoredTraceabilityChecksum, ...
this.profGetBuildDir(lAnchorFolder), ...
this.ComponentRef, lIsTask,...
sampleTime);
end

function addIdentifier(this, lProbeType, id, fcnName)
lProbeTypeName = coder_profile_ProbeType.toString(uint32(lProbeType));
this.addInternalSectionId(lProbeTypeName, id);

this.checkMaxIdLimit(lProbeTypeName);

% add probe to ProbeCalls
this.instrumentationFnc(...
lProbeType,...
id);

lModelTraceInfo.CodeName = fcnName;
lModelTraceInfo.CallSiteSids = {};
lModelTraceInfo.CallSiteNames = {};
lModelTraceInfo.ProfSecsSampleTime = [];

this.updateProfilingSections(lProbeTypeName,...
lModelTraceInfo);
end

function numKeys = getNumIdentifiers(this)
numKeys = 0;

allKeys = this.SectionIds.keys;
for i = 1 : length(allKeys)
numKeys = numKeys + length(this.SectionIds(allKeys{i}));
end
end

function lSectionIds = getSectionIdsForCodeName(this, probeType, lCodeName)
lSectionIds = uint64(zeros(1,0));
lProbeTypeName = coder_profile_ProbeType.toString(uint32(probeType));
if ~this.ProfSecsCodeName.isKey(lProbeTypeName) ||...
~this.SectionIds.isKey(lProbeTypeName)
return;
end

idx = strcmp(this.ProfSecsCodeName(lProbeTypeName), lCodeName);
allSectionIds = this.SectionIds(lProbeTypeName);
lSectionIds = allSectionIds(idx);
end

function active = isActive(~, ~)
% By default, a component registry is assumed to be active. This can be
% overridden by subclasses which require a more flexibility.
active = true;
end

function setBuildInfoFileName(this, lBuildInfo)
this.BuildInfoFileName = lBuildInfo;
end

function buildInfoFileName = getBuildInfoFileName(this)
buildInfoFileName = this.BuildInfoFileName;
end
end

methods (Access=public, Hidden)
function res = getWrapperPostfix(~)
res = '_wrapper';
end
end

methods (Static=true, Access=public)

function reportInfoFile = getReportInfoFile(buildDir)

reportInfoFile = fullfile(buildDir,'html','reportInfo.mat');

if isempty(dir(reportInfoFile))
reportInfoFile = '';
end
end

function checksum = getTraceabilityChecksumForBuildDir(buildDir)

checksum = '';
if exist(buildDir, 'dir')
try
reportInfo = rtw.report.getReportInfo('',buildDir);
checksum = sprintf('%bx', reportInfo.TimeStamp);
catch ME
if strcmp(ME.identifier, 'RTW:report:invalidBuildFolder') ...
|| strcmp(ME.identifier, 'RTW:report:ReportInfoNotFound')
checksum = '';
else
rethrow(ME);
end
end
end
end

end

end