% This class is only used for GPU profiling with nsys.
classdef (Hidden = true) GPUHostLauncher < rtw.connectivity.HostLauncher
%

%   Copyright 2018-2023 The MathWorks, Inc.

    properties (Constant, Access = 'private')
        dataFile = gpucoder.internal.profiling.GpuProfilerFileNames.REPORT_BASE;
        profileOptions = {'--sample=none', '--cpuctxsw=none', '--force-overwrite=true'};
    end

    properties (Access = 'private')
        serverPortFile = '';
    end

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

        function [command, commandArgs] = getCommand(this)
            % get the original command
            [origCommand, origCommandArgs] = getCommand@rtw.connectivity.HostLauncher(this);

            assert(iscell(origCommandArgs), 'Must use setArgs cell array style arguments!');

            % wrap the original command with Nsys
            if ispc
                [exists, ~] = system('where nsys.exe');
                if exists ~= 0
                    error(message('gpucoder:system:no_nsight_systems_on_path'));
                end
                nsysArgs = ['profile', ...
                            '--trace=cuda,nvtx', ...
                            '-o', this.dataFile, ...
                            this.profileOptions];
            else
                [exists, ~] = system('which nsys');
                if exists ~= 0
                    error(message('gpucoder:system:no_nsight_systems_on_path'));
                end
                nsysArgs = ['profile', ...
                            '--trace=cublas,cuda,cudnn,nvtx', ...
                            '-o', this.dataFile, ...
                            this.profileOptions];
            end
            command = 'nsys';
            commandArgs = [nsysArgs origCommand origCommandArgs];
            if this.needsToWriteServerPortToFile
                % Appending -server_info_file to dump the server port
                % to a file due to g2917664
                commandArgs = [commandArgs '-server_info_file' this.getServerPortFileName];
            end
        end

        function serverPort = pollForServerPortNumber(this, extendTimeout)
             arguments
                this
                extendTimeout = false;
             end
            if this.needsToWriteServerPortToFile
                serverPort = coder.connectivity.PILCommunication.parseOutputFileForHostServerPort(this.getExePID, ...
                    this.getServerPortFileName, ...
                    extendTimeout);
                deleteServerPortFile = onCleanup(@() delete(this.getServerPortFileName));
            else
                serverPort = pollForServerPortNumber@rtw.connectivity.HostLauncher(this, extendTimeout);
            end
        end

        function timeOut = getTimeoutForTermination(~)
	     % nsys profiling may take a long time to write the report. The report will not be
            % produced if the app gets forcefully terminated.
            timeOut = 600;
        end
    end

    methods (Access = 'private')
        function serverPortFileName = getServerPortFileName(this)
            if isempty(this.serverPortFile)
                [~, exeName, ~] = fileparts(this.getExe);
                this.serverPortFile = [tempname, '_', exeName, '_serverPort.info'];
            end
            serverPortFileName = this.serverPortFile;
        end

        function result = needsToWriteServerPortToFile(~)
            result = ispc;
        end
    end

end

% LocalWords:  cpuctxsw nsys gpucoder nsight cuda nvtx cublas cudnn
