classdef (Sealed) TimeProbeComponentRegistry < coder.profile.ProbeComponentRegistry
% TimeProbeComponentRegistry is the main class to register time probes

% Copyright 2016-2023 The MathWorks, Inc.

properties (Access = private)
TaskInfo;
end

methods

function this = TimeProbeComponentRegistry(componentName,...
componentRef,...
targetWordSize,...
buildDir,...
maxIdLength,...
codeFolderRelative)
if nargin < 6
codeFolderRelative = '';
end
this = this@coder.profile.ProbeComponentRegistry(componentName,...
componentRef,...
targetWordSize,...
buildDir,...
maxIdLength,...
codeFolderRelative);
this.registerProbeType(coder_profile_ProbeType.TASK_TIME_PROBE);
this.registerProbeType(coder_profile_ProbeType.EXEC_TIME_PROBE);
this.registerProbeType(coder_profile_ProbeType.FILTER_TIME_PROBE);
this.registerProbeType(coder_profile_ProbeType.TASK_OVERRUN_PROBE);
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

methods(Access = public)

function lTaskInfo = getTaskInfo(this)
lTaskInfo = this.TaskInfo;
end

% Re-implement this function in this way:
% - introduce a protected method in ProbeComponentRegistry to check if a
% probe type is present (isProbeTypePresent)
% - re-implement this function to return isProbeTypePresent(v)
function flag = isTaskRegistry(this)
lProbeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.TASK_TIME_PROBE);
flag = this.isProbeTypePresent(lProbeTypeName);
end

% xxx(g1088733) - remove this function once we remove traceability from
% registries
function id = requestIdentifierForTask(...
this,...
functionName,...
taskInfo,...
lFullComponentPath)
if isempty(this.ComponentRef)
this.ComponentRef = lFullComponentPath;
else
assert(strcmp(this.ComponentRef, lFullComponentPath),...
'The component path must remain consistent');
end

%Generate a name describing the profiled section of code.
%The name is a composition of function name, sample period and
%sample offset.
%Task names evaluated here are used in both SDI and profiling
%report
extFunctionName = this.getExtendedTaskName(functionName,...
taskInfo.samplePeriod,...
taskInfo.sampleOffset);

id = this.requestSectionId(...
coder_profile_ProbeType.TASK_TIME_PROBE,...
this.ComponentName,...
extFunctionName,...
taskInfo.taskName,...
taskInfo.samplePeriod,...
taskInfo.sampleOffset);

% fill in local SectionIds
probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.TASK_TIME_PROBE);
this.addInternalSectionId(probeTypeName, id);

this.checkMaxIdLimit(probeTypeName);

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

lModelTraceInfo.CodeName = extFunctionName;
lModelTraceInfo.CallSiteSids = {};
lModelTraceInfo.CallSiteNames = {taskInfo.taskName};
lModelTraceInfo.ProfSecsSampleTime = [taskInfo.samplePeriod taskInfo.sampleOffset];

if isempty(this.TaskInfo)
this.TaskInfo = struct('sectionId', {}, 'priority', {});
end
this.TaskInfo(end+1).sectionId = id;
if isfield(taskInfo, 'taskPrio')
this.TaskInfo(end).priority = taskInfo.taskPrio;
else
this.TaskInfo(end).priority = 0;
end

this.updateProfilingSections(probeTypeName, lModelTraceInfo);
end

% insert an EXEC_TIME_PROBE using EDG instrumentation
function [sectionId, profileFcns] = insertExecTimeProbe...
(this,...
codeName,...
fileName,...
lineNumStart,...
lineNumStop,...
sid)
probeType = coder_profile_ProbeType.EXEC_TIME_PROBE;
probeTypeName = coder_profile_ProbeType.toString(...
coder_profile_ProbeType.EXEC_TIME_PROBE);
sectionId = this.requestSectionId...
(probeType, codeName, this.ComponentName);
% get all callsite names (i.e.: name of the block that the code was
% generated from)
if ~isempty(sid)
handles = Simulink.ID.getHandle(sid);
callSiteName = cell(1, length(handles));
for i=1:length(handles)
callSiteName{i} = get_param(handles{i}, 'Name');
end
else
callSiteName = cell(1, 1);
callSiteName{1} = codeName;
end
callSiteName = {callSiteName};
sid = {sid};
traceInfo = struct('CodeName', codeName,...
'CallSiteSids', sid,...
'CallSiteNames', callSiteName);
profileFcns = this.addProbeIdentifier...
(probeType, probeTypeName, sectionId, traceInfo);

% we are adding to intermediate data structure information
% that states which line in a file we are profiling with a
% given section id
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

% insert an EXEC_TIME_PROBE using EDG instrumentation
function [sectionId, profileFcns] = insertExecTimeProbeNoSimulink...
(this,...
codeName,...
fileName,...
lineNumStart,...
lineNumStop,...
sid)
probeType = coder_profile_ProbeType.EXEC_TIME_PROBE;
probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.EXEC_TIME_PROBE);
sectionId = this.requestSectionId(probeType, codeName, this.ComponentName);
callSiteNameArr = cell(1, 1);
callSiteNameArr{1} = codeName;
sid = {sid};
traceInfo = struct('CodeName', codeName, 'CallSiteSids', {sid}, 'CallSiteNames', {callSiteNameArr});
profileFcns = this.addProbeIdentifier(probeType, probeTypeName, sectionId, traceInfo);

% we are adding to intermediate data structure information
% that states which line in a file we are profiling with a
% given section id
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

function [sectionId, profileFcns] = insertFilterProbe(this, codeName)
probeType = coder_profile_ProbeType.FILTER_TIME_PROBE;
probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.FILTER_TIME_PROBE);
sectionId = this.requestSectionId(probeType, codeName);
callSiteNameArr = {codeName};
sid = {''};
traceInfo = struct('CodeName', codeName, ...
'CallSiteSids', {sid}, 'CallSiteNames', ...
{callSiteNameArr});
profileFcns = this.addProbeIdentifier(probeType, probeTypeName, sectionId, traceInfo);
end

function [id, probeFnc] = requestTaskOverrunProbe(this, taskname)
id = this.requestSectionId(coder_profile_ProbeType.TASK_OVERRUN_PROBE, ...
this.ComponentName, taskname);

% fill in local SectionIds
probeTypeName = coder_profile_ProbeType.toString(coder_profile_ProbeType.TASK_OVERRUN_PROBE);
this.addInternalSectionId(probeTypeName, id);

this.checkMaxIdLimit(probeTypeName);

% add probe to ProbeCalls
probeFnc = this.instrumentationFnc(coder_profile_ProbeType.TASK_OVERRUN_PROBE, id);
end
end
end

% LocalWords: EDG