# 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
+
+
+
+ Function |
+ Time (s) |
+ Time (%) |
+ Calls |
+
+
+
+%entries
+
+
+
+
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
+
+
+
+ Function |
+ Total (s) |
+ Self (s) |
+ Calls |
+
+
+
+%entries
+
+
+
+
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)