- Undocumented Matlab - https://undocumentedmatlab.com -

Function definition meta-info

Posted By Yair Altman On September 4, 2013 | 6 Comments

Last week, Loren Shure posted an article [1] explaining some documented ways to retrieve information about the type of Matlab functions. Loren basically showed how we can use a combination of the built-in nargin and exist functions to check whether a specified function-name is a regular m-file, a script filename, a class name or something else.
Today I will discuss several additional alternatives for retrieving information about a specified function:

### The mtree() alternative

Reader Mark Brown has commented [7] about an alternative, using the semi-documented built-in function mtree:

>> t = mtree('mtree.m','-file'); t.select(1).kind  % m-class file
ans =
CLASSDEF
>> t = mtree('profile.m','-file'); t.select(1).kind  % regular m-file function
ans =
FUNCTION
>> t = mtree('test.m','-file'); t.select(1).kind  % script file
ans =
EXPR


mtree contains numerous other goodies in its reported parse-tree, including information about who-calls-what-where, set/used info about variables and other information used in the lexical parsing of the m-file. mtree is a very sophisticated m-file function (or rather, a class), but is very heavily documented internally, and so it can definitely be read and followed. Unfortunately, much of the internal processing is carried out by opaque internal c-functions (mtreemex), but there is still plenty of processing left for the 3K+ lines of m-code. I plan to write an extensive article about this function one day.
Unlike many other internal m-files, which are much simpler, less well written and sparsely documented (if at all), but for which personal credit is included in a comment, mtree includes no personal credit although it appears to be very well written and documented. I have to congratulate the unknown author(s) for both their proficiency and humility. Addendum: This unknown author is now confirmed to have been Steve Johnson [8], author of the mlint [9] set of tools and functionality.
For people worried about future compatibility, note that mtree has existed on the Matlab path (%matlabroot%\toolbox\matlab\codetools\@mtree\mtree.m) since 2006 (I think R2006a, but I’m not certain; it already had version 1.3 by R2007b; the latest version [R2013b] is 2.5). Considering the large investment in its code thus far, and the significant effort that it would take to create a replacement, I don’t see this function going away in the near future. Then again, there is no certainty about this, and it might well disappear without notice in some future Matlab release.

### The getcallinfo() alternative

getcallinfo is another semi-documented internal function, which uses mtree to construct an m-file’s call-tree and reports the results in either a tabulated manner (if no output arg is request), or a struct array (as output arg):

>> getcallinfo('profile.m');
Name           Type             Starts Ends Length Full Name
----           ----             ------ ---- ------ ---------
profile        function             1   242    242 profile
ParseInputs    subfunction        247   354     82 profile>ParseInputs
ParseOption    nested-function    262   287     26 profile>ParseInputs/ParseOption
notifyUI       subfunction        356   370     15 profile>notifyUI
>> s = getcallinfo('profile.m')
s =
1x4 struct array with fields:
type
name
fullname
functionPrefix
calls
firstline
lastline
>> s(4).calls
ans =
fcnCalls: [1x1 struct]
innerCalls: [1x1 struct]
dotCalls: [1x1 struct]
atCalls: [1x1 struct]
>> s(4).calls.fcnCalls
ans =
names: {'usejava'}
lines: 359
>> s(4).calls.innerCalls
ans =
names: {1x0 cell}
lines: [1x0 double]
>> s(4).calls.dotCalls
ans =
names: {'com.mathworks.mde.profiler.Profiler.start'  [1x40 char]  [1x41 char]}
lines: [363 365 367]


Note: In order to use getcallinfo, we first need to fix a small internal bug in %matlabroot%/toolbox/matlab/codetools/getcallinfo.m, specifically within the displayStructure() sub-function. Modifying this file may require administrator privileges, and can be done in any text editor. The bug is that the keyword length is used as a variable in this function (line #136 in R2013b), and so cannot be used to reference the built-in function length(). We can either rename the variable (lines 136, 139, 145) or the function. It’s easiest to rename length() to numel() in line #147 (highlighted below):

130:   function displayStructure(strc)
...
136:       length = getString(message('MATLAB:codetools:reports:RptLength'));
137:       fullName = getString(message('MATLAB:codetools:reports:RptFullName'));
138:
139:       fprintf('%-20s %-20s %-4s %-4s %-6s %-20s\n',name,type,starts,ends,length,fullName);
140:       fprintf('%-20s %-20s %-4s %-4s %-6s %-20s\n', ...
...
145:           getDashesForString(length), ...
146:           getDashesForString(fullName));
147:       for n = 1:numel(strc)   % original code: n = 1:length(strc)
...
152:       end
153:   end


### The mlint() alternative

A few months ago, I wrote [9] about mlint‘s undocumented interface and ability to report much internal information about the analyzed file. It is no surprise that one of the undocumented mlint options is -calls, which lists the calling-tree of an m-file. For example:

>> mlint profile.m -calls
M0 1 14 profile
E0 242 3 profile
U1 122 15 callstats
U1 131 11 ParseInputs
U1 133 10 MException
U1 134 5 throw
U1 161 8 lower
U1 164 9 notifyUI
U1 165 23 true
U1 169 23 false
U1 180 9 profreport
U1 183 13 usejava
U1 184 13 error
U1 184 19 message
U1 188 21 profile
U1 189 16 isempty
U1 192 17 profview
U1 236 9 warning
S0 248 7 ParseInputs
E0 354 3 ParseInputs
U1 260 1 error
U1 260 7 nargchk
U1 260 17 Inf
U1 260 21 nargin
N1 262 23 ParseOption
E1 287 7 ParseOption
U2 263 12 strcmp
...


In this report, the first character represents the function type:

• M = main (top-level) function
• S = sub-function
• N = nested function
• U = out-of-scope (external/built-in) function
• A = anonymous function
• E = end-of-function indication

The following numbers indicate the nesting level, line #, column # and function identifier (name). In essence, it’s the same information presented by getcallinfo, with two distinctions: getcallinfo‘s report is much more nicely formatted, and on the other hand getcallinfo does not include internal function-calls (maybe there’s an undocumented switch that I haven’t found for this).
In this regard, I wish to once again praise Urs Schwartz’s excellent farg [10] and fdep [11] utilities, which use this undocumented mlint syntax. They seem to out-perform and out-class the built-in depends function by a wide margin…

### The which() alternative

The built-in which function can be used to report the file-path of m-file functions, or an indicate that the function is built-in (i.e., coded as a C/C++ function in one of the Matlab libraries):

>> str = which('perfTest')
str =
C:\Yair\Books\MATLAB Performance Tuning\Code\perfTest.m
>> str = which('matlab.io.MatFile')
str =
C:\Program Files\Matlab\R2013b\toolbox\matlab\iofun\+matlab\+io\MatFile.m
>> str = which('sin')
str =
built-in (C:\Program Files\Matlab\R2013b\toolbox\matlab\elfun\@double\sin)
>> str = which('noSuchFunction')
str =
''


Note: a little-known option enables specifying sub-functions (although this does not work for nested functions for some unknown reason [bug? oversight?]):

>> str = which('nestedFuncName','in','MFileName');


### The functions() alternative

Similar functionality can be achieved via the built-in functions, using function handles rather than function names:

>> functions(@perfTest)
ans =
function: 'perfTest'
type: 'simple'
file: 'C:\Yair\Books\MATLAB Performance Tuning\Code\perfTest.m'
>> functions(@matfile)
ans =
function: 'matfile'
type: 'simple'
file: 'C:\Program Files\Matlab\R2013b\toolbox\matlab\iofun\matfile.m'
>> fType = functions(@(a)a+1)
fType =
function: '@(a)a+1'
type: 'anonymous'
file: ''
workspace: {[1x1 struct]}
>> functions(@transpose)
ans =
function: 'transpose'
type: 'simple'
file: ''


Unlike which, functions can also be used for both sub- and nested-functions:

% The following was called within the confines of a specific m-file function:
K>> fType = functions(@MFileName)
fType =
function: 'MFileName'
type: 'simple'
file: 'C:\Yair\MFileName.m'
K>> fType = functions(@subFunc)
fType =
function: 'subFunc'
type: 'scopedfunction'
file: 'C:\Yair\MFileName.m'
parentage: {'subFunc'  'MFileName'}
K>> fType = functions(@nestedFunc)
fType =
function: 'MFileName/nestedFunc'
type: 'nested'
file: 'C:\Yair\MFileName.m'
workspace: {[1x1 struct]}


Note that parentage and workspace are undocumented sub-fields of the returned struct: they are mentioned in the official doc page [12], but only in passing, without a format explanation. Also note that workspace is a cell array of a single element (contrary to the official doc – this is probably an internal bug), containing the actual workspace as a struct (fields = workspace variables). So it should be accessed as fType.workspace{1}.varName. Note that parentage and workspace are present only for certain types of function types.
Have you found any other nifty ways of retrieving function meta-info in run-time? Are you using such meta-info in an interesting manner? If so, please post a short comment below.

Categories: Medium risk of breaking in future versions, Semi-documented function, Stock Matlab function

6 Comments To "Function definition meta-info"

#1 Comment By Mark Brown On September 4, 2013 @ 12:59

Yair,

I just posted on The Mathworks FileExchange, a function for finding file dependencies that uses MTREE. It should show up in a day or two as File ID# 43370. I would appreciate any feedback. Thanks.

Mark Brown

#2 Comment By Some Guy On September 5, 2013 @ 07:08

I believe that this is the author of mtree():

[8]

#3 Comment By Yair Altman On September 7, 2013 @ 11:11

Thanks – I have independently verified this and updated the article text accordingly. I think that it’s a great loss to MathWorks and Matlab in general that Steve no longer works on Matlab.

#4 Pingback By Parsing mlint (Code Analyzer) output | Undocumented Matlab On September 7, 2013 @ 11:14

[…] much more detailed information about the nature of functions can be found using the semi-documented mtree function (or rather, Matlab class: %matlabroot%/toolbox/matlab/codetools/@mtree/mtree.m). This is a huge […]

#5 Comment By Another guy On November 6, 2014 @ 06:24

Is there any new/additional information available on the mtree function?

#6 Comment By Yair Altman On November 6, 2014 @ 07:01

Not to my knowledge, but you’re welcome to dig in the code and if you find anything useful please post a followup comment here.

URL to article: https://undocumentedmatlab.com/articles/function-definition-meta-info

URLs in this post:

[1] posted an article: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/

[2] mtree: http://undocumentedmatlab.com/blog/function-definition-meta-info/#mtree

[3] getcallinfo: http://undocumentedmatlab.com/blog/function-definition-meta-info/#getcallinfo

[4] mlint: http://undocumentedmatlab.com/blog/function-definition-meta-info/#mlint

[5] which: http://undocumentedmatlab.com/blog/function-definition-meta-info/#which

[6] functions: http://undocumentedmatlab.com/blog/function-definition-meta-info/#functions

[7] commented: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/#comment-38909

[8] Steve Johnson: http://en.wikipedia.org/wiki/Stephen_C._Johnson

[9] mlint: http://undocumentedmatlab.com/blog/parsing-mlint-code-analyzer-output/

[10] farg: http://www.mathworks.com/matlabcentral/fileexchange/15924-farg-a-pedestrian-m-file-parser-showing-all-used-functions-syntax

[11] fdep: http://www.mathworks.com/matlabcentral/fileexchange/17291-fdep-a-pedestrian-function-dependencies-finder

[12] doc page: http://www.mathworks.com/help/matlab/ref/functions.html

[13] Function call timeline profiling : https://undocumentedmatlab.com/articles/function-call-timeline-profiling

[14] Sparse data math info : https://undocumentedmatlab.com/articles/sparse-data-math-info