function s = profile(varargin)
%PROFILE Profile function execution time.
%   PROFILE ON starts the profiler and clears previously recorded
%   profile statistics.
%
%   PROFILE takes the following options:
%
%      -DETAIL LEVEL
%         This option specifies the set of functions for which
%         profiling statistics are gathered.  If LEVEL is 'mmex'
%         (the default), then information about M-functions,
%         M-subfunctions, and MEX-functions is recorded.  If
%         LEVEL is 'builtin', then in addition the profiler
%         records information about builtin functions such as
%         EIG.
%
%      -TIMER CLOCK
%         This option specifies the type of time to be used in profiling.
%         If CLOCK is 'cpu' (the default), then compute time is measured.
%         If CLOCK is 'real', then wall-clock time is measured.  For
%         example, the function PAUSE will have very small cpu time, but
%         real time that accounts for the actual time paused.
%
%      -HISTORY
%         If this option is specified, MATLAB records the exact
%         sequence of function calls so that a function call
%         history report can be generated.  NOTE: MATLAB will
%         not record more than 10000 function entry and exit
%         events.  However, MATLAB will continue recording
%         other profiling statistics after this limit has been
%         reached.
%
%      Options may appear either before or after ON in the same command,
%      but they may not be changed if the profiler has been started in a
%      previous command and has not yet been stopped.
%
%   PROFILE OFF stops the profiler.
%
%   PROFILE VIEWER stops the profiler and opens the graphical profile browser.
%   The output for PROFILE VIEWER is an HTML file in the Profiler window.
%   The file listing at the bottom of the function profile page shows four
%   columns to the left of each line of code.
%         Column 1 (red) is total time spent on the line in seconds.
%         Column 2 (blue) is number of calls to that line.
%         Column 3 is the line number
%
%   PROFILE RESUME restarts the profiler without clearing
%   previously recorded function statistics.
%
%   PROFILE CLEAR clears all recorded profile statistics.
%
%   S = PROFILE('STATUS') returns a structure containing
%   information about the current profiler state.  S contains
%   these fields:
%
%       ProfilerStatus   -- 'on' or 'off'
%       DetailLevel      -- 'mmex' or 'builtin'
%       Timer            -- 'cpu' or 'real'
%       HistoryTracking  -- 'on' or 'off'
%
%   STATS = PROFILE('INFO') suspends the profiler and returns
%   a structure containing the current profiler statistics.
%   STATS contains these fields:
%
%       FunctionTable    -- structure array containing stats
%                           about each called function
%       FunctionHistory  -- function call history table
%       ClockPrecision   -- precision of profiler time
%                           measurement
%       ClockSpeed       -- Estimated clock speed of the cpu (or 0)
%       Name             -- name of the profiler (i.e. MATLAB)
%
%   The FunctionTable array is the most important part of the STATS
%   structure. Its fields are:
%
%       FunctionName     -- function name, includes subfunction references
%       FileName         -- file name is a fully qualified path
%       Type             -- M-function, MEX-function
%       NumCalls         -- number of times this function was called
%       TotalTime        -- total time spent in this function
%       Children         -- FunctionTable indices to child functions
%       Parents          -- FunctionTable indices to parent functions
%       ExecutedLines    -- array detailing line-by-line details (see below)
%       IsRecursive      -- is this function recursive? boolean value
%
%   The ExecutedLines array has several columns. Column 1 is the line
%   number that executed. If a line was not executed, it does not appear in
%   this matrix. Column 2 is the number of times that line was executed,
%   and Column 3 is the total spent on that line. Note: The sum of Column 3 does
%   not necessarily add up to the function's TotalTime.
%
%   If you want to save the results of your profiler session to disk, use
%   the PROFSAVE command.
%
%   Examples:
%
%       profile on
%       plot(magic(35))
%       profile viewer
%       profsave(profile('info'),'profile_results')
%
%       profile on -history
%       plot(magic(4));
%       p = profile('info');
%       for n = 1:size(p.FunctionHistory,2)
%           if p.FunctionHistory(1,n)==0
%               str = 'entering function: ';
%           else
%               str = ' exiting function: ';
%           end
%           disp([str p.FunctionTable(p.FunctionHistory(2,n)).FunctionName]);
%       end
%
%   See also PROFSAVE, PROFVIEW.

%   Copyright 1984-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.12 $  $Date: 2004/10/25 14:47:42 $

% This code exists to inhibit profiling of functions called by
% profile.m, except, of course, callstats.m, which is suppressed
% from the view (see profview.m).
initialState = callstats('status');
callstats('stop');
enableAtEnd = strcmp(initialState, 'on');

[action, detailLevel, timerIndex, history, msg] ...
  = ParseInputs(initialState, varargin{:});

if (~isempty(msg))
    error(msg);
end

if detailLevel > 0
    callstats('level', detailLevel);
end

if timerIndex > 0
    callstats('timer', timerIndex);
end

if history
    callstats('history', history);
end

switch action
    case 'on'
        callstats('clear');
        notifyUI('start');
        callstats('start');

    case 'off'
        notifyUI('stop');
        enableAtEnd = false;

    case 'resume'
        notifyUI('start');
        enableAtEnd = true;

    case 'clear'
        callstats('clear');
        notifyUI('clear');

    case 'report'
        profreport

    case 'viewer'
        if ~usejava('mwt')
            error('The profiler requires the Java VM');
        else
            enableAtEnd = false;

            stats = profile('info');
            if isempty(stats.FunctionTable)
                com.mathworks.mde.profiler.Profiler.invoke;
            else
                notifyUI('stop');
                profview(0,stats);
            end
        end

    case 'status'
        s.ProfilerStatus = initialState;
        switch callstats('level')
            case 1
                s.DetailLevel = 'mmex';

            case 2
                s.DetailLevel = 'builtin';
        end
        switch callstats('timer')
            case 1
                s.Timer = 'cpu';

            case 2
                s.Timer = 'real';
        end
        if (callstats('history'))
            s.HistoryTracking = 'on';
        else
            s.HistoryTracking = 'off';
        end

    case 'info'
        [ft,fh,cp,name,cs] = callstats('stats');
        s.FunctionTable = ft;
        s.FunctionHistory = fh;
        s.ClockPrecision = cp;
        s.ClockSpeed = cs;
        s.Name = name;

    case 'none'
        % Nothing to do

    otherwise
        warning('MATLAB:profile:ObsoleteSyntax', ...
            'Unknown argument for PROFILE. See HELP PROFILE.');
end

if (enableAtEnd)
    callstats('resume');
end
end

%%%
%%% ParseInputs
%%%
function [action, level, clock, history, msg] ...
    = ParseInputs(initialState, varargin)
%PARSEINPUTS Parse user's input arguments.

% Defaults
action = 'none';
level = 0;
clock = 0;
history = 0;

msg = nargchk(2,Inf,nargin);
if (~isempty(msg))
    return;
end

    function option = ParseOption(optionname, argname, options)
    if strcmp(initialState, 'on');
        msg = sprintf('The profiler has already been started.  %s cannot be changed.',...
                      optionname);
        option = 0;
    elseif k == length(varargin)
        msg = sprintf('%s must follow -%s option', argname, optionname);
        option = 0;
    else
        k = k + 1;
        option = strmatch(lower(varargin{k}), options);
        if (isempty(option))
            msg = sprintf('Invalid %s setting.', argname);
        elseif (length(option) > 1)
            msg = sprintf('Ambiguous %s setting.', argname);
        end
    end
    end
      
% Walk the input argument list
k = 1;
while (k <= length(varargin))
    arg = varargin{k};
    if (~ischar(arg) || isempty(arg))
        msg = 'Invalid input.';
        return;
    end

    if (arg(1) == '-')
        % It's an option
        options = {'detail', 'timer', 'history'};
        idx = strmatch(lower(arg(2:end)), options);
        if (isempty(idx))
            msg = 'Unknown option.';
            return;
        end
        if (length(idx) > 1)
            msg = 'Ambiguous option.';
            return;
        end

        option = options{idx};
        switch option
            case 'detail'
                level = ParseOption('DETAIL','LEVEL',{'mmex','builtin'});
                if not(isempty(msg)), return; end

            case 'timer'
                clock = ParseOption('TIMER','CLOCK',{'cpu','real'});
                if not(isempty(msg)), return; end

            case 'history'
                history = 1;

            otherwise
                msg = 'Unknown option.';
                return;
        end

    else
        % It's an action
        action = arg;
    end

    k = k + 1;
end
end

function notifyUI(action)

if usejava('mwt')
    import com.mathworks.mde.profiler.Profiler;
    switch action
        case 'start'
            Profiler.start;
        case 'stop'
            Profiler.stop;
        case 'clear'
            Profiler.clear;
    end
end
end
