function [execProfTimerData, profData, executionProfile] = executionTimeAnalyzeHelper...
    (timerData,  sectionIds, ...
     onTargetMetrics, ...
     bufferIncrement, doDestroy, summaryOnly, coreNum, threadId, ...
     isRealTime, taskSectionIds, taskPriorities, taskSuspendIds, ...
     minContextSwitch, lGlobalRegistry, lModelName,...
     lTimerTicksPerSecond, lJitterThreshold_in, lCodeDir, lInstrumentedCodeDir, ...
     simTimes, taskNames, taskSampleTimes, documentationLinkParameters, ...
     maxTimerAdjustment)
% Helper function to create execution profile timer data

%   Copyright 2011-2020 The MathWorks, Inc.

numCores = max(coreNum)+1;
identifierClass=class(sectionIds);
timerClass=class(timerData);

% Default value for jitter threshold
lJitterThreshold = lJitterThreshold_in;
if isempty(lJitterThreshold_in)
    % Set default value
    if numCores <= 1
        lJitterThreshold = 0;
    else
        lJitterThreshold = uint64(intmax(class(timerData))/16);
    end
end

lModelForProfilingTopSettings = '';
lModelAtTopOfCodeGenHierarchy = '';
sectionIdMax = uint32(intmax(identifierClass)/2-1);
timerMax = uint64(intmax(timerClass));

% If global registry is empty, ensure that section ids are re-mapped to occupy a
% contiguous address space starting at 1
reMap = isempty(lGlobalRegistry);

% get the task and code section ids
[taskSectionIds, taskSuspendIds, codeSectionIds, sectionIds] = ...
    i_get_code_and_task_ids...
    (taskSectionIds, taskSuspendIds, sectionIds, reMap);

if isempty(lGlobalRegistry)
    lGlobalRegistry = i_create_registry...
        (taskSectionIds, codeSectionIds, taskNames, taskSampleTimes, taskPriorities);
end

[taskSectionIds, taskPriorities] = lGlobalRegistry.getTaskInfo(coder_profile_ProbeType.TASK_TIME_PROBE);

assert(length(taskPriorities)==length(taskSectionIds), 'Lengths must match')

profData = coder.profile.execTimeAnalyze(...
    uint32(sectionIds),...
    uint64(timerData),...
    uint32(coreNum),...
    uint32(threadId),...
    double(doDestroy), ...
    sectionIdMax, timerMax,...
    uint64(bufferIncrement), ...
    boolean(summaryOnly), ...
    uint32(numCores), ...
    uint32(isRealTime),...
    uint64(lJitterThreshold), ...
    uint32(taskSectionIds),...
    uint32(taskPriorities),...
    uint32(taskSuspendIds),...
    uint64(minContextSwitch),...
    double(simTimes), ...
    uint64(maxTimerAdjustment));

if ~ischar(profData)
    if summaryOnly
        saveOpts = 'SummaryOnly';
    else
        saveOpts = 'AllData';
    end
    
    % Add summary metrics
    profData.onTargetMaxVals = onTargetMetrics;
    profData.TaskSuspendSectionIds = taskSuspendIds;
    
    % Normal behaviour
    execProfTimerData = coder.profile.ExecTimeData(profData);
    execProfTimerData.CodeProfilingSaveOptions = saveOpts;
    execProfTimerData.ModelForProfilingTopSettings = lModelForProfilingTopSettings;
    execProfTimerData.OriginalComponentName = lModelAtTopOfCodeGenHierarchy;
    execProfTimerData.TimerTicksPerSecond = lTimerTicksPerSecond;
    lReportFcn = 'coder.internal.SLCExecTimeReport';
    executionProfile = coder.profile.ExecutionTime...
        (profData, lReportFcn, documentationLinkParameters);
    
    executionProfile.InstrCodeSubFolder = lInstrumentedCodeDir;
    
    execProfTimerData.initialize(lGlobalRegistry);
    executionProfile.RealTime = isRealTime;
    if summaryOnly
        executionProfile.CodeProfilingSaveOptions = 'SummaryOnly';
    else
        executionProfile.CodeProfilingSaveOptions = 'AllData';
    end
    executionProfile.OriginalComponentName = lModelName;
    executionProfile.TimerTicksPerSecond = lTimerTicksPerSecond;
    executionProfile.GlobalRegistry = lGlobalRegistry;
    executionProfile.CodeDir = lCodeDir;
    
    if ~isempty(lModelName)
        % Update traceability data for mapping back to the model (issues warning if
        % CodeDir is not valid)
        traceabilityUpdate(executionProfile);
    end
    
else
    % We've called the destroy method
    execProfTimerData=profData;
    executionProfile = [];
end


function [taskSectionIds_out, taskSuspendIds_out, ...
          codeSectionIds_out, allSectionIdData_out] ...
    = i_get_code_and_task_ids...
    (taskSectionIds, taskSuspendIds, allSectionIdData, reMap)

% Determine all the unique start of section ids
idClass=class(allSectionIdData);
idMax = intmax(idClass);
allSectionIds = unique(allSectionIdData);
maxSectionId = feval(idClass, (idMax-1)/2);
endIdx = allSectionIds>maxSectionId;
allSectionIds(endIdx) = ...        
    bitcmp(allSectionIds(endIdx)); % Map all end of section ids to the
                                   % corresponding start of section id
allSectionIds = unique(allSectionIds);

allSectionIds = union(feval(idClass, taskSectionIds), allSectionIds);

% Filter out any sectionIds that are zero
zeroIdx = allSectionIds==0;
allSectionIds = allSectionIds(~zeroIdx);    

if isempty(taskSectionIds)
    % Assume all identifiers are for tasks
    taskSectionIds = allSectionIds;
end

% Determine the code section ids
codeSectionIds = setdiff(allSectionIds, taskSectionIds);

if reMap
    id1 = feval(idClass, 1);
    idN = feval(idClass, length(allSectionIds));
    taskSectionIds_out = feval(idClass, zeros(size(taskSectionIds)));
    taskSuspendIds_out = feval(idClass, zeros(size(taskSuspendIds)));
    codeSectionIds_out = feval(idClass, zeros(size(codeSectionIds)));
    allSectionIdData_out = feval(idClass, zeros(size(allSectionIdData)));
    for i = id1:idN
        nStart = allSectionIdData==allSectionIds(i);
        nEnd = allSectionIdData==bitcmp(allSectionIds(i));
        allSectionIdData_out(nStart) = i;
        allSectionIdData_out(nEnd) = bitcmp(i);
        nTaskStart = taskSectionIds==allSectionIds(i);
        taskSectionIds_out(nTaskStart) = i;
        nSuspendStart = taskSuspendIds==allSectionIds(i);
        taskSuspendIds_out(nSuspendStart) = i;
        nCodeStart = codeSectionIds==allSectionIds(i);
        codeSectionIds_out(nCodeStart) = i;
    end
else
    taskSectionIds_out = taskSectionIds;
    taskSuspendIds_out = taskSuspendIds;
    codeSectionIds_out = codeSectionIds;
    allSectionIdData_out = allSectionIdData;    
end


function lGlobalRegistry = i_create_registry...
    (taskSectionIds, codeSectionIds, taskNames, taskSampleTimes, taskPriorities)



if isempty(taskPriorities)
    taskPriorities = uint32(1:length(taskSectionIds));
else
    taskPriorities = uint32(taskPriorities);
    assert(length(taskPriorities)==length(taskSectionIds),...
           'Task priorities and task section identifiers must have same length');
end

maxTaskId = max(taskSectionIds);

tmpmodel = '';
lTargetWordSize = 32;
lCodeDir = '';
lTaskRegistry = coder.profile.TimeProbeComponentRegistry...
    ('', '', lTargetWordSize, lCodeDir, []);
lFullComponentPath=tmpmodel;

taskPrioritiesExpanded = 1:max(taskSectionIds);
taskPrioritiesExpanded(taskSectionIds)=taskPriorities;

for i=1:max(taskSectionIds)
    
    idx = find(i==taskSectionIds, 1, 'first');
    
    taskPriority = taskPrioritiesExpanded(idx);    
    
    if ~isempty(taskNames)
        functionName = taskNames{idx};
    else
        functionName=['task_fcn_name' num2str(idx)];
    end
    if ~isempty(taskSampleTimes)
        T = taskSampleTimes{idx};
        samplePeriod = T(1);
        sampleOffset = T(2);
    else
        samplePeriod = NaN;
        sampleOffset = NaN;
    end        
    taskInfo = struct('taskName', functionName,...
        'samplePeriod', samplePeriod, ...
        'sampleOffset', sampleOffset,...
        'taskPrio', taskPriority);
    lTaskRegistry.requestIdentifierForTask(functionName, ...
        taskInfo, lFullComponentPath);
end

if ~isempty(codeSectionIds)
    assert(min(codeSectionIds)>maxTaskId);
    lCodeFolder='';
    lMaxIdLength=[];
    lComponentRegistry = coder.profile.TimeProbeComponentRegistry...
        (tmpmodel, tmpmodel, lTargetWordSize,...
         lCodeFolder, ...
         lMaxIdLength);

    for i=1:max(codeSectionIds)
        % Code section
        fcnName=['profiled_section_' num2str(i)];
        id = lComponentRegistry.requestSectionId(coder_profile_ProbeType.EXEC_TIME_PROBE, fcnName, tmpmodel);
        lComponentRegistry.addIdentifier(coder_profile_ProbeType.EXEC_TIME_PROBE, id, fcnName);
    end
else
    lComponentRegistry = [];
end

lAnchorFolder = '';
lRelativePathToAnchor = '';
lGlobalRegistry = coder.profile.ProbeGlobalRegistry...
    (tmpmodel, lAnchorFolder, lRelativePathToAnchor);
lGlobalRegistry.addRegistries({lComponentRegistry lTaskRegistry});


