classdef (Hidden = true) MemCheckHostLauncher < rtw.connectivity.HostLauncher
%MEMCHECKHOSTLAUNCHER launches the SIL application in Valgrind.

%   Copyright 2019 The MathWorks, Inc.

    properties (Constant, Access = 'private')
        MemoryErrorFoundFlag = 'xilLeakError:';
        LogFileExtension = '.vlog';
    end

    properties(Access = 'private')
        LogFileNamePrefix;
    end

    methods
        % constructor
        function this = MemCheckHostLauncher(varargin)
            % call super class constructor
            this@rtw.connectivity.HostLauncher(varargin{:});
            % validate configuration
            this.validate;
        end

        function [command, commandArg] = getCommand(this)
            % Partially construct the log file name:
            this.LogFileNamePrefix = fullfile(this.getComponentArgs.getApplicationCodePath, ...
                strcat(this.getComponentArgs.getComponentCodeName, '.rpid'));
            % The flags used in the PreCommand are taken from sbmemcheck tool:
            valgrindPreCommand = ['valgrind --tool=memcheck --num-callers=48 --alignment=16',...
                ' --leak-check=full --track-origins=yes --log-file="' this.getLogFileName('%p') '"',...
                ' --show-leak-kinds=definite --errors-for-leak-kinds=all',...
                ' --keep-stacktraces=alloc-and-free --leak-resolution=high --demangle=yes',...
                ' --error-limit=no --gen-suppressions=all --partial-loads-ok=no --max-stackframe=2500000',...
                ' --fullpath-after=' ' --error-markers=' this.MemoryErrorFoundFlag];

            % Construct an extended launcher with the Valgrind PreCommand.
            command = rtw.connectivity.extendedLaunchCommand(this.getExe, 'PreCommand', valgrindPreCommand);
            commandArg = this.argString;
        end
    end

    methods(Access = 'protected')
        % Reconstruct log file name, read it and display detected errors, if any.
        function processExeArtifacts(this, currentExePID)
            logFileName = this.getLogFileName(currentExePID);
            logFileText = fileread(logFileName);
            if this.isMemoryLeakDetected(logFileText)
                this.dispInfo(logFileText);
                logFileLink = this.getLogFileNameAsLink(logFileName);
                DAStudio.error('PIL:pil:SILValgrindMemoryErrorDetected', logFileLink);
            end
        end
    end

    methods(Access = 'private')
        % Check if the logFile contains error by searching for a particular
        % flag inserted in the log file
        function isMemoryLeakDetected = isMemoryLeakDetected(this, logFileText)
            isMemoryLeakDetected = contains(logFileText, this.MemoryErrorFoundFlag);
        end
        % Construct the log file name using the prefix and the extension:
        function logFileName = getLogFileName(this, exePID)
            logFileName = strcat(this.LogFileNamePrefix, num2str(exePID), this.LogFileExtension);
        end
        % Construct a link to the log file:
        function link = getLogFileNameAsLink(~, logFileName)
            cmd = ['edit ' logFileName];
            link = targets_hyperlink_manager('new', logFileName, cmd);
        end
        
        function validate(this)
            % validate the current configuration
            if ispc || ismac
                % error out if not glnxa64 
                DAStudio.error('PIL:pil:SILValgrindUnsupportedArch')
            end
            
            if strcmpi(this.getComponentArgs.getParam('SILDebugging'), 'on')
                % error out if SIL debugging is enabled
                DAStudio.error('PIL:pil:SILValgrindUnsupportedSILDebugging')
            end
        end
    end
end