## Copyright (C) 2015 Daniel Kraft ## ## This file is part of Octave. ## ## Octave is free software; you can redistribute it and/or modify it ## under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 3 of the License, or (at ## your option) any later version. ## ## Octave is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with Octave; see the file COPYING. If not, see ## . ## -*- texinfo -*- ## @deftypefn {Function File} {} profexport (@var{dir}, @var{name}, @var{data}) ## @deftypefnx {Function File} {} profexport (@var{dir}, @var{name}) ## @deftypefnx {Function File} {} profexport (@var{dir}, @var{data}) ## @deftypefnx {Function File} {} profexport (@var{dir}) ## ## Export profiler data as HTML. ## ## Export the profiling data in @var{data} into a series of HTML ## files in the folder @var{dir}. The initial file will be ## @address@hidden/index.html}. ## ## If @var{name} is specified, it must be a string that contains a ``name'' ## for the profile being exported. This name is included in the HTML. ## ## The input @var{data} is the structure returned by @code{profile ("info")}. ## If unspecified, @code{profexport} will use the current profile dataset. ## ## @seealso{profshow, profexplore, profile} ## @end deftypefn ## Built-in profiler. ## Author: Daniel Kraft function profexport (dir, name, data) if (nargin > 3) print_usage (); endif if (!ischar (dir)) print_usage (); endif if (nargin == 1) data = profile ("info"); name = ""; elseif (nargin == 2) if (isstruct (name)) data = name; name = ""; else if (!ischar (name)) print_usage (); endif data = profile ("info"); endif endif if (!exist (dir, "dir")) ok = mkdir (dir); if (!ok) error ("failed to create output directory '%s'", dir); endif endif if (length (name) > 0) % FIXME: Escape! name = sprintf ("Profile - %s", name); else name = "Profile"; endif __writeFlat (fullfile (dir, "index.html"), name, data.FunctionTable); for i = 1 : length (data.FunctionTable) __writeFunc (fullfile (dir, sprintf ("func-%d.html", i)), name, ... data.FunctionTable, i); endfor top = struct ("name", "Top"); __writeHierarchical (dir, name, data.FunctionTable, ... {top}, data.Hierarchical, 1); endfunction % TODO: Tests and demos. ################################################################################ # Write flat profile. function __writeFlat (file, name, table) template = "\ \n\ \n\ \n\ %title\n\ \n\ \n\

Flat %title

\n\

hierarchical profile

\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ %entries\n\ \n\
FunctionTime (s)Time (%)Calls
\n\ \n\ \n"; entryTemplate = "\ \n\ %name\n\ %timeabs\n\ %timerel\n\ %calls\n\ \n"; % Construct the entries string. This follows the same logic % that is used in profshow. times = [ table.TotalTime ]; totalTime = sum (times); [~, p] = sort (times, "descend"); entries = ""; for i = 1 : length (table) row = table(p(i)); cur = entryTemplate; cur = strrep (cur, "%num", sprintf ("%d", p(i))); % FIXME: Implement escaping! cur = strrep (cur, "%name", row.FunctionName); cur = strrep (cur, "%timeabs", sprintf ("%.3f", row.TotalTime)); cur = strrep (cur, "%timerel", ... sprintf ("%.2f", 100 * row.TotalTime / totalTime)); cur = strrep (cur, "%calls", sprintf ("%d", row.NumCalls)); entries = [entries, cur]; endfor % Build full page content. res = template; res = strrep (res, "%title", name); res = strrep (res, "%entries", entries); % Write out the file. __writeToFile (file, res); endfunction ################################################################################ # Write "function profile" pages. function __writeFunc (file, name, table, ind) row = table(ind); template = "\ \n\ \n\ \n\ %title\n\ \n\ \n\

Function %name

\n\

flat profile

\n\
\n\
Attributes:
\n\
%attr
\n\
Total time:
\n\
%timeabs seconds
\n\
Number of calls:
\n\
%calls
\n\
Called by:
\n\
%parents
\n\
Calling:
\n\
%children
\n\
\n\ \n\ \n"; % Fill in basic data. res = template; res = strrep (res, "%title", name); % FIXME: Escape! res = strrep (res, "%name", row.FunctionName); res = strrep (res, "%timeabs", sprintf ("%.3f", row.TotalTime)); res = strrep (res, "%calls", sprintf ("%d", row.NumCalls)); % Build up attribute list. attr = ""; if (row.IsRecursive) attr = "recursive"; endif res = strrep (res, "%attr", attr); % Add parent and child list. parents = __buildParentOrChildList (table, row.Parents); res = strrep (res, "%parents", parents); children = __buildParentOrChildList (table, row.Children); res = strrep (res, "%children", children); % Write out the file. __writeToFile (file, res); endfunction function lst = __buildParentOrChildList (table, inds) if (length (inds) == 0) lst = "none"; return; endif template = "%name"; lst = ""; for i = 1 : length (inds) if (i > 1) lst = [lst, ", "]; endif cur = template; cur = strrep (cur, "%num", sprintf ("%d", inds(i))); % FIXME: Escape! cur = strrep (cur, "%name", table(inds(i)).FunctionName); lst = [lst, cur]; endfor endfunction ################################################################################ # Write a hierarchical profile page. % In order to generate unique filenames for the pages, we keep a running % counter that is passed through and updated by the recursive calls. % The function returns two counter values: The one that is chosen % for its own page (so that parent nodes can link down to them) % and the next value to be passed to the next call. function [mine, cnt] = __writeHierarchical (dir, name, funcs, ... parents, children, cnt) template = "\ \n\ \n\ \n\ %title\n\ \n\ \n\

Hierarchical %title

\n\

flat profile

\n\

%parents

\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ %entries\n\ \n\
FunctionTotal (s)Self (s)Calls
\n\ \n\ \n"; entryTemplate = "\ \n\ %name\n\ %total\n\ %self\n\ %calls\n\ \n"; % Fill in basic data and parent breadcrumbs. res = template; res = strrep (res, "%title", name); parentsStr = __hierarchicalParents (parents); res = strrep (res, "%parents", parentsStr); % Set this page's counter and update parents struct with it. mine = cnt++; parents{end}.cnt = mine; file = sprintf ("%s/hierarchy-%d.html", dir, mine); % Sort children by time. times = -[ children.TotalTime ]; [~, p] = sort (times); children = children(p); % Recurse on children and construct entry list. entries = ""; for i = 1 : length (children) cur = children(i); curName = funcs(cur.Index).FunctionName; newParents = parents; newParents{end + 1} = struct ("name", curName); [childCnt, cnt] = __writeHierarchical (dir, name, funcs, ... newParents, cur.Children, cnt); str = entryTemplate; str = strrep (str, "%cnt", sprintf ("%d", childCnt)); % FIXME: Escape! str = strrep (str, "%name", curName); str = strrep (str, "%total", sprintf ("%.3f", cur.TotalTime)); str = strrep (str, "%self", sprintf ("%.3f", cur.SelfTime)); str = strrep (str, "%calls", sprintf ("%d", cur.NumCalls)); entries = [entries, str]; endfor res = strrep (res, "%entries", entries); % Write out the file. __writeToFile (file, res); endfunction function str = __hierarchicalParents (parents) % We always have at least the "Top" entry! assert (length (parents) > 0); template = "%name"; lastTemplate = "%name"; str = ""; for i = 1 : length (parents) - 1 cur = template; cur = strrep (cur, "%cnt", sprintf ("%d", parents{i}.cnt)); % FIXME: Escape! cur = strrep (cur, "%name", parents{i}.name); str = [str, cur, " > "]; endfor cur = lastTemplate; % FIXME: Escape! cur = strrep (cur, "%name", parents{end}.name); str = [str, cur]; endfunction ################################################################################ # General helper functions. function __writeToFile (file, str) fid = fopen (file, "w"); if (fid < 0) error ("failed to open '%s' for writing", file); endif fputs (fid, str); fclose (fid); endfunction