classdef (Hidden = true) StackProbeTarget < coder.profile.TargetInterface
%STACKPROBETARGET generates the API for TASK_STACK_PROBE and EXEC_STACK_PROBE

%   Copyright 2020-2023 The MathWorks, Inc.
    
    properties
        FunctionStart;
        FunctionEnd;
        useInlineAsm;
        IsCatch = false;
    end
    
    methods(Static)
        function obj = CreateStackExecProbeTarget(functionStart, functionEnd)
            obj = coder.profile.StackProbeTarget(functionStart, functionEnd);
        end
        
        function obj = CreateStackTaskProbeTarget(functionStart, functionEnd)
            obj = coder.profile.StackProbeTarget(functionStart, functionEnd);
        end

        function obj = CreateStackOffsetProbeTarget(functionStart)
            obj = coder.profile.StackProbeTarget(functionStart, '');
        end

        function obj = CreateStackCatchProbeTarget(functionStart)
            obj = coder.profile.StackProbeTarget(functionStart, '');
            obj.IsCatch = true;
        end
    end
    
    methods(Access=private)
        function this = StackProbeTarget(functionStart, functionEnd)
            this.FunctionStart = functionStart;
            this.FunctionEnd = functionEnd;
            this.useInlineAsm = ~ispc;
        end
    end
    
    methods
        function targetWriteSectionFcns(this, writer, uploadFcn, hasExecuted, ~)
            lIdType = this.IdentifierCDataType;

            if ~hasExecuted
                this.writeUnusedMacro(writer);
            end

            if this.IsCatch
                this.writeFcnsNotCallingStack(writer, uploadFcn, lIdType);
            else
                this.writeFcnsCallingStack(writer, uploadFcn, lIdType);
            end
        end
        
        function targetWriteProbeExportedFcnPrototypes(this, writer, hasExecuted) %#ok<INUSD>
        end
        
        function targetWriteInit(this, hasExecuted) %#ok<INUSD>
        end
        
        function targetWriteSectionExportedIncludes(this, writer)
            
            % Basic type headers
            basicTypeIncludeLines = coder.profile.getBasicTypeIncludeLines(this.CodeDescriptor);
            for i=1:length(basicTypeIncludeLines)
                writer.writeLine(basicTypeIncludeLines{i});
            end
            writer.newLine;
            
            lStackDriver = this.GlobalRegistry.ProfilingStackDriver;
            if ~isempty(lStackDriver) && ~isempty(lStackDriver.HeaderFile)
                writer.writeCellLines...
                    ({'/* Functions with a C call interface */';
                    '#ifdef __cplusplus';
                    'extern "C" {';
                    '#endif'});
                writer.writeLine('#include "%s"', lStackDriver.HeaderFile);
                writer.writeCellLines...
                    ({'#ifdef __cplusplus';
                    '}';
                    '#endif'});
            end
        end
    end
    
    methods(Access = private)
        function writeUnusedMacro(~, writer)
            writer.newLine;
                unusedParamMacro = {
                    '/* A function parameter may be intentionally unused */'
                    '#ifndef UNUSED_PARAMETER  '
                    '#if defined(__LCC__)  '
                    '#define UNUSED_PARAMETER(x)'
                    '#else'
                    '#define UNUSED_PARAMETER(x) (void) (x) '
                    '#endif'
                    '#endif'};
                writer.writeCellLines(unusedParamMacro);
                writer.newLine;
        end

        function writeFcnsCallingStack(this, writer, uploadFcn, lIdType)
            % write implementation for instrumentation API which upload the
            % stack register 

            hasUploadFcn = ~isempty(uploadFcn);
            if hasUploadFcn
                lPtrType = this.GlobalRegistry.ProfilingStackDriver.getCodeType();
                lPtrFcn = this.GlobalRegistry.ProfilingStackDriver.DriverFunction;
            end
            
            % start method
            sectionIdVar = 'sectionId';
            fcnSignature = sprintf('void %s(%s %s)',  ...
                this.FunctionStart, lIdType, sectionIdVar);
            writer.wBlockStart('%s', fcnSignature);
            if hasUploadFcn
                writer.wLine('%s val;', lPtrType);
                writer.wLine('val = %s();', lPtrFcn);
                writer.wLine('%s((void *)(&val), sizeof(%s), %s);', uploadFcn, lPtrType, sectionIdVar);
            else
                writer.wLine('UNUSED_PARAMETER(%s);', sectionIdVar);
            end
            writer.wBlockEnd();

            % end method
            if ~isempty(this.FunctionEnd)
                fcnSignature = sprintf('void %s(%s %s)',this.FunctionEnd, lIdType, sectionIdVar);
                writer.wBlockStart('%s', fcnSignature);
                if hasUploadFcn
                    writer.wLine('%s val;', lPtrType);
                    writer.wLine('%s sectionIdNeg = ~%s;', lIdType, sectionIdVar);
                    writer.wLine('val = %s();', lPtrFcn);
                    writer.wLine('%s((void *)(&val), sizeof(%s), sectionIdNeg);', uploadFcn, lPtrType);
                else
                    writer.wLine('UNUSED_PARAMETER(%s);', sectionIdVar);
                end
                writer.wBlockEnd();
            end
        end

        function writeFcnsNotCallingStack(this, writer, uploadFcn, lIdType)
            % write implementation for instrumentation API which doesn't
            % upload the stack register. This API is meant to notify that
            % an exception is cought and we need to unwind the 
            % function-call stack

            hasUploadFcn = ~isempty(uploadFcn);
            lPtrType = this.GlobalRegistry.ProfilingStackDriver.getCodeType();
            
            sectionIdVar = 'sectionId';
            offsetVar = 'offset';
            fcnSignature = sprintf('void %s(%s %s, %s %s)',  ...
                this.FunctionStart, lIdType, sectionIdVar, lIdType, offsetVar);
            
            writer.wBlockStart('%s', fcnSignature);
            
            if hasUploadFcn
                writer.wLine('%s var = %s;', lPtrType, offsetVar);
                writer.wLine('%s((void *)(&var), sizeof(%s), %s);', uploadFcn, lPtrType, sectionIdVar);
            else
                writer.wLine('UNUSED_PARAMETER(%s);', sectionIdVar);
            end
            
            writer.wBlockEnd();
        end
    end
end
