function s = profile(varargin)
%PROFILE MATLAB function profiler.
%   PROFILE ON starts the profiler and clears previously recorded
%   profile statistics.
%
%   PROFILE ON may be followed by one or more options.  These
%   options include:
%
%      -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.  If LEVEL is 'operator', then in addition the
%         profiler records information about builtin operators
%         such as '+'.
%
%      -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.
%
%   PROFILE OFF suspends the profiler.
%
%   PROFILE RESUME restarts the profiler without clearing
%   previously recorded function statistics.
%
%   PROFILE CLEAR clears all recorded profile statistics.
%
%   PROFILE REPORT generates a profile report in HTML format,
%   saves it to a temporary file, and displays it in your web
%   browser.  PROFILE REPORT BASENAME uses the specified base
%   filename to save the report.  Note that the HTML report
%   consists of several different files.  Because of this,
%   BASENAME should not have an extension.  PROFILE REPORT
%   suspends the profiler.
%
%   PROFILE PLOT displays a bar graph of the functions with the
%   most execution time.
%
%   S = PROFILE('STATUS') returns a structure containing
%   information about the current profiler state.  S contains
%   these fields:
%
%       ProfilerStatus   -- 'on' or 'off'
%       DetailLevel      -- 'mmex', 'builtin', or 'operator'
%       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 
%       Name             -- name of the profiler (i.e. MATLAB)
%
%   See the online reference page entry for PROFILE for more
%   information about the FunctionTable and FunctionHistory
%   fields.
%
%   Example:
%
%       profile on -detail builtin
%       rgb = imread('ngc6543a.jpg');
%       profile report
%
%   See also PROFREPORT.

%   Copyright 1984-2000 The MathWorks, Inc. 
%   $Revision: 1.29 $  $Date: 2000/07/21 17:33:42 $

initialState = callstats('status');
callstats('stop');
enableAtEnd = strcmp(initialState, 'on');

[action, detailLevel, history, fname, msg] = ParseInputs(varargin{:});
if (~isempty(msg))
    error(msg);
end

switch action
    case 'on'
        callstats('stop');
        callstats('clear');
        callstats('level', detailLevel);
        callstats('history', history);
        callstats('start');
        
    case 'off'
        callstats('stop');
        enableAtEnd = 0;
        
    case 'resume'
        callstats('resume');
        
    case 'clear'
        callstats('clear');
        
    case 'report'
        callstats('stop');
        enableAtEnd = 0;
        
        stats = profile('info');
        if isempty(stats.FunctionTable)
            error(sprintf('%s\n%s\n%s', ...
                          'MATLAB''s internal profiler logs are empty, so no report',...
                          'can be generated.  Sometimes this is caused by forgetting',...
                          'to call PROFILE ON.  See the reference page for PROFILE.'));
        end
        profreport(fname, stats);
	
    case 'plot'
        callstats('stop');
        enableAtEnd = 0;
        
        structArray = callstats('stats');
        GeneratePlot(structArray);
        
    case 'status'
        s.ProfilerStatus = initialState;     
        switch callstats('level')
            case 1
                s.DetailLevel = 'mmex';
                
            case 2
                s.DetailLevel = 'builtin';
                
            case 3
                s.DetailLevel = 'operator';
        end
        if (callstats('history'))
            s.HistoryTracking = 'on';
        else
            s.HistoryTracking = 'off';
        end
        
    case 'info'
        enableAtEnd = 0;
        [ft,fh,cp,name] = callstats('stats');
        s.FunctionTable = ft;
        s.FunctionHistory = fh;
        s.ClockPrecision = cp;
        s.Name = name;

    case 'none'
        % Nothing to do
        
    otherwise
        warning(sprintf('%s \n%s \n%s', ...
                        'PROFILE FILENAME is an obsolete usage.', ...
                        'Use PROFILE ON instead.  See HELP PROFILE.'));
end

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

%%%
%%% GeneratePlot
%%%
function GeneratePlot(structArray)
%GENERATEPLOT Generate bar plot of most time-consuming functions.

if (isempty(structArray))
    error('Empty function log.');
end

structArray = MungeNames(structArray);

times = [structArray.TotalTime]';
[times, sortIdx] = sort(times);
structArray = structArray(sortIdx);
times = flipud(times);
structArray = flipud(structArray);
structArray(11:end) = [];
times(11:end) = [];

barh(times);
set(gca, 'Position', [0.3 0.11 0.69 0.88]);
set(gca, 'XLim', [0 max(1, 1.1*max(times))]);
set(gca, 'YTick', 1:length(times));
set(gca, 'YTickLabel', {structArray.FunctionName});
set(gca, 'YDir','reverse')
xlabel('Total time (seconds)')

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

% Defaults
action = 'none';
level = 1;
history = 0;
fname = '';
msg = '';

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

% Walk the input argument list
k = 1;
while (k <= nargin)
    arg = varargin{k};
    if (~ischar(arg) | isempty(arg))
        msg = 'Invalid input.';
        return;
    end
        
    if (arg(1) == '-')
        % It's an option
        options = {'detail', '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'
                if (k == nargin)
                    msg = 'LEVEL must follow -DETAIL option';
                    return;
                end
                
                k = k + 1;
                levels = {'mmex', 'builtin', 'operator'};
                level = strmatch(lower(varargin{k}), levels);
                if (isempty(level))
                    msg = 'Invalid LEVEL setting.';
                    return;
                elseif (length(level) > 1)
                    msg = 'Ambiguous LEVEL setting.';
                    return;
                end

            case 'history'
                history = 1;
                
            otherwise
                msg = 'Unknown option.';
                return;
        end
        
    else
        % It's an action
        action = varargin{k};
        
        if (strcmp(action, 'report'))
            if (k < nargin)
                fname = varargin{k+1};
                k = k + 1;
            else
                fname = tempname;
            end
        end
    end
    
    k = k + 1;
end


function structArray = MungeNames(structArray)
%MUNGENAMES Strip out the path information for private functions.

s = ['private' filesep];

for k = 1:length(structArray)
    idx = findstr(s, structArray(k).FunctionName);
    if (~isempty(idx))
        structArray(k).FunctionName = structArray(k).FunctionName(idx(end) + ...
                        length(s):end);
    end
end

