# HG changeset patch # User Daniel Kraft # Date 1462817823 -7200 # Mon May 09 20:17:03 2016 +0200 # Node ID 5e4d1adc519b6fd24741a8bf625e3903297feb72 # Parent fa0462ab5c201941e3adc74cf8dcb331e8510594 Add profexport to write profiler output as HTML. * scripts/profiler/profexport.m: New function. * scripts/profiler/html: New directory with supporting templates/data. * scripts/profiler/module.mk: Add necessary files to build system. diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/flat.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/flat.html Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,25 @@ + + + + + %title + + + +

Flat %title

+

hierarchical profile

+ + + + + + + + + + +%entries + +
FunctionTime (s)Time (%)Calls
+ + diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/flat_entry.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/flat_entry.html Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,6 @@ + + %name + %timeabs + %timerel + %calls + diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/function.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/function.html Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,24 @@ + + + + + %title + + + +

Function %name

+

flat profile

+
+
Attributes:
+
%attr
+
Total time:
+
%timeabs seconds
+
Number of calls:
+
%calls
+
Called by:
+
%parents
+
Calling:
+
%children
+
+ + diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/hierarchical.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/hierarchical.html Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,26 @@ + + + + + %title + + + +

Hierarchical %title

+

flat profile

+

%parents

+ + + + + + + + + + +%entries + +
FunctionTotal (s)Self (s)Calls
+ + diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/hierarchical_entry.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/hierarchical_entry.html Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,6 @@ + + %name + %total + %self + %calls + diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/html/style.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/html/style.css Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,24 @@ +/* +Copyright (C) 2016 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 +. +*/ + +tr:nth-of-type(odd) +{ + background-color: #ccc; +} diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/module.mk --- a/scripts/profiler/module.mk Thu May 05 11:19:59 2016 +0200 +++ b/scripts/profiler/module.mk Mon May 09 20:17:03 2016 +0200 @@ -3,12 +3,21 @@ scripts_profiler_FCN_FILES = \ scripts/profiler/profexplore.m \ + scripts/profiler/profexport.m \ scripts/profiler/profile.m \ scripts/profiler/profshow.m scripts_profilerdir = $(fcnfiledir)/profiler +scripts_profiler_DATA = $(scripts_profiler_FCN_FILES) -scripts_profiler_DATA = $(scripts_profiler_FCN_FILES) +scripts_profiler_htmldir = $(octetcdir)/profiler +scripts_profiler_html_DATA = \ + scripts/profiler/html/flat.html \ + scripts/profiler/html/flat_entry.html \ + scripts/profiler/html/function.html \ + scripts/profiler/html/hierarchical.html \ + scripts/profiler/html/hierarchical_entry.html \ + scripts/profiler/html/style.css FCN_FILES += \ $(scripts_profiler_FCN_FILES) diff -r fa0462ab5c20 -r 5e4d1adc519b scripts/profiler/profexport.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/profiler/profexport.m Mon May 09 20:17:03 2016 +0200 @@ -0,0 +1,303 @@ +## Copyright (C) 2015-2016 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 (!copyfile (__dataFilename ("style.css"), dir)) + error ("failed to copy data file to directory '%s'", dir) + endif + + if (length (name) > 0) + name = sprintf ("Profile - %s", __escapeHtml (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 + +################################################################################ +# Write flat profile. + +function __writeFlat (file, name, table) + template = __readTemplate ("flat.html"); + entryTemplate = __readTemplate ("flat_entry.html"); + + % 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))); + cur = strrep (cur, "%name", __escapeHtml (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) + template = __readTemplate ("function.html"); + row = table(ind); + + % Fill in basic data. + res = template; + res = strrep (res, "%title", name); + res = strrep (res, "%name", __escapeHtml (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))); + cur = strrep (cur, "%name", __escapeHtml (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 = __readTemplate ("hierarchical.html"); + entryTemplate = __readTemplate ("hierarchical_entry.html"); + + % 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)); + str = strrep (str, "%name", __escapeHtml (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)); + cur = strrep (cur, "%name", __escapeHtml (parents{i}.name)); + str = [str, cur, " > "]; + endfor + + cur = lastTemplate; + cur = strrep (cur, "%name", __escapeHtml (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 + +function fn = __dataFilename (name) + etcdir = __octave_config_info__ ("octetcdir"); + fn = fullfile (etcdir, "profiler", name); +endfunction + +function str = __readTemplate (name) + fn = __dataFilename (name); + str = fileread (fn); +endfunction + +function str = __escapeHtml (str) + str = strrep (str, "&", "&"); + str = strrep (str, "<", "<"); + str = strrep (str, ">", ">"); + str = strrep (str, "\"", """); +endfunction + +################################################################################ +# Tests and demo. + +%!demo +%! profile on; +%! A = rand (100); +%! B = expm (A); +%! profile off; +%! dir = tempname (); +%! profexport (dir, "Example Profile"); +%! printf ("Open %s/index.html to browse the profile.\n", dir); + +%!error profexport () +%!error profexport (1) +%!error profexport (1, 2, 3, 4) +%!error profexport ("dir", 5)