Automating Diagnostic Instrumentation

Dear List,

I have some questions about some passes for LLVM that I'm thinking about
but first I need to give a little background ...

One of the things I do in my "day job" (large IT system performance
analysis) is to figure out where to place diagnostic instrumentation
within an application. The instrumentation I'm talking about here is the
kind that is inserted into application code to capture things like
wall-clock latency, cpu time, number of i/o requests, etc. For accurate
diagnosis, it is often necessary to collect that kind of data about
every occurrence of some event or event pair (like entry/exit of a
function). However, this kind of instrumentation can have very high
overhead if used liberally. When an application is under heavy load, it
is critical to select the instrumentation points correctly so as to
minimize overhead and maximize the utility of the information provided.
Fortunately, there is a way to do this automatically. By constructing a
static call graph of the application, it is possible to discover the
"fan-out" points. Fan-out points are calls in the call chain that are
called from very few places (typically one) but directly or indirectly
call many functions. That is, some functions in an application are at
the top of the call chain (e.g. like "main" which implies all the
processing of the program) while others are at the bottom (like "strlen"
which implies no further calls and is a leaf node in the call chain). In
between these two extremes (root and leaf), the call chain will fan-in
(like strlen) and fan-out (like main). Architecturally, we can speak of
the fan-out points being the places in the application code where a
module boundary is being crossed. By instrumenting these fan-out points
we can be assured that we're instrumenting things that (a) have
architectural significance (i.e. good quality of information) and (b)
imply enough processing that the overhead of instrumentation is
negligible.

With the above kind of instrumentation in mind, I consider such
auto-instrumentation as "just another pass" in LLVM. That is, I believe
LLVM makes it pretty easy to do the call graph analysis, find the
"fan-out" points, and insert the instrumentation code. So, given that
its relatively easy to do this, I have the following questions for the
list:

(1) Would others find this kind of diagnostic instrumentation useful?

(2) Would it be useful to turn the call graph data ino a pretty picture
via graphviz (both statically and dynamically) ?

(3) How much of this auto-instrumentation pass is already written in
existing passes?

(4) Are there other ways to achieve the same ends?

(5) Can someone give me some tips on how to implement this? It would be
my first LLVM pass.

On a side note, there are source language constructs for which I'm
wanting to aggregate the data captured by the instrumentation.. Call
chains of functions are great but sometimes you can't see the forest for
the trees because the information is too dense. What is useful is to
aggregate the performance data at higher levels of abstraction. For
example, in a multi-threaded, object-oriented program you might want to
see aggregation at the process, thread, class, object instance, and
method levels. In order to instrument for these kinds of aggregated
views, I need to be able to provide some kind of "context" (process id,
thread id, class, etc.) in which a data point is captured. This implies
that I need the instrumentation pass to understand some things about the
source-level language. and possibly capture information about the
environment the instrumentation application will run in. Unfortunately,
that means that the pass would either become language specific or
environment specific because I would have to ensure that the source
language compiler added the necessary constructs to the generated code
to provide the contextual information needed by the pass. This raises a
couple questions more questions:

(1) Is there be a general mechanism to communicate information between
source language compiler and LLVM pass? If there isn't, should there
be? In the case I describe above it would be *highly* useful (IMO) to
have the source language compiler provide source level information for a
language independent pass to use later.

(2) Is there any existing mechanism in LLVM for providing (1) directly?
What I'm thinking of is some kind of API that the source language
compiler can use to add addtional information that any subsequent pass
might need to use.

Reid.

(2) Would it be useful to turn the call graph data ino a pretty picture
via graphviz (both statically and dynamically) ?

Sure: analyze -print-callgraph foo.bc

(3) How much of this auto-instrumentation pass is already written in
existing passes?

There are various instrumentation passes already in LLVM, for example, to
trace instructions as they execute, to insert profiling instrumentation,
etc. These live in lib/Transforms/Instrumentation/

(4) Are there other ways to achieve the same ends?

I dunno. It sounds like a good approach. :slight_smile:

(5) Can someone give me some tips on how to implement this? It would be
my first LLVM pass.

I would take a look at other passes that do similar things. For example,
look at clients of the CallGraph class and look at a couple of the
instrumentation classes. If you know what code you want to insert, it
should be fairly straight-forward.

(1) Is there be a general mechanism to communicate information between
source language compiler and LLVM pass?

No, not at present.

If there isn't, should there be? In the case I describe above it would
be *highly* useful (IMO) to have the source language compiler provide
source level information for a language independent pass to use later.

Sure, again, it's a matter of designing the feature so that it is broadly
applicable and fits in with the LLVM "style". If the feature made sense
in the context of LLVM, extending LLVM isn't a problem. How to get more
source-level information into LLVM is an open question.

(2) Is there any existing mechanism in LLVM for providing (1) directly?
What I'm thinking of is some kind of API that the source language
compiler can use to add addtional information that any subsequent pass
might need to use.

Not yet. If you're interested, you check out the debugger documentation.
In the case of the debugger, the source language front-end packages up
language-specific information and passes it through LLVM. LLVM doesn't
understand or interpret this information though, the debugger process does
in the end.

-Chris

Reid,

Adding this kind of instrumentation pass would be very valuable in LLVM. We don't have any such thing at present, and there could be multiple uses for it.

Joel Stanley did an MS thesis last year that could complement this kind of pass nicely. Joel's thesis was on dynamic performance instrumentation guided by explicit queries within the application (I will forward you a copy). Think about it as two things:

(1) A simple performance query language that allows queries to be embedded within an application (e.g., "how many L2 cache misses does the loop nest labeled X incur, in those instances when a moving average cost for function Y is more than 3x of long-term average, i.e., the function Y has been unusually slow"). The language allows the user to define arbitrary "metrics," to specify routines that can be used to measure those metrics, and then to query those metrics for arbitrary points or intervals within the application. A number of common computational performance metrics and their measurement routines are predefined, e.g., elapsed user/total/system time, L1/L2 cache misses, TLB misses. Many more predefined ones can be added, including OS, networking, and other kinds of metrics.

The query language is actually implemented as a simple API. Joel wrote an LLVM pass that recognizes calls to this API, and replaces them with initial calls to his runtime system.

(2) A sophisticated runtime system that dynamically inserts and removes calls to instrumentation routines that actually do the measurement. This is driven by the requirements of the actual queries, e.g., for the example query above, you would insert instrumentation for L2 cache misses around loop nest X.

Your automatic pass could potentially use Joel's runtime support to do the actual work of inserting and removing instrumentation -- the pass would only have to insert the appropriate queries in our query "language" API.

Caveat: the runtime instrumentation library has only been lightly tested and isn't robust yet.

--Vikram
http://www.cs.uiuc.edu/~vadve
http://llvm.cs.uiuc.edu/

Vikram,

Thanks for the salient feedback. This sounds _very_ interesting. I'm
reading Joel's thesis in my "spare" time. At 75 pages it might take some
time. I'll respond in more detail when I have a better understanding of
what Joel has done and what support is already in LLVM.

Reid.

Sure: analyze -print-callgraph foo.bc

Cool. Didn't know about that.

There are various instrumentation passes already in LLVM, for example, to
trace instructions as they execute, to insert profiling instrumentation,
etc. These live in lib/Transforms/Instrumentation/
...
I would take a look at other passes that do similar things. For example,
look at clients of the CallGraph class and look at a couple of the
instrumentation classes. If you know what code you want to insert, it
should be fairly straight-forward.

Okay, will do. I didn't know we had a CallGraph class already!

Reid.

Hi Reid,

Reid Spencer wrote:

With the above kind of instrumentation in mind, I consider such auto-instrumentation as "just another pass" in LLVM. That is, I believe LLVM makes it pretty easy to do the call graph analysis, find the "fan-out" points, and insert the instrumentation code. So, given that its relatively easy to do this, I have the following questions for the
list:

First, thanks for your brilliant summary of instrumentation for performance analysis. I am currently working on program structure analysis for regression test selection and minimization, and found many similar points to what you describe.

(1) Would others find this kind of diagnostic instrumentation useful?

I would: it occurs that whenever a program grows big, it is crucial to get measures from selected indicators, at least to identify performance bottlenecks or do coverage analysis (which parts of the code were used during a program execution). Instrumentation is a cornerstone for properly testing a program, without it you can't get your test coverage and thus lack a fundamental information to properly select which tests are relevant to (re)-validate your program.

(2) Would it be useful to turn the call graph data ino a pretty picture via graphviz (both statically and dynamically) ?

It may, but it may even be more interesting to output the call graph to a database which can be later processed. For instance, the gcc-introspector projet (http://introspector.sf.net) outputs to an RDF database, from which many things can be done. For some programs (if not many), the call graphs will be too complex.You can see some "static call graphs" (resulting from static analysis) of a GCC source file I made here <http://people.type-z.org/seb/bordel/sched.png&gt;\. And this is only a one-level call graph of one source file.

(snip)
This implies
that I need the instrumentation pass to understand some things about the
source-level language. and possibly capture information about the environment the instrumentation application will run in. Unfortunately,
that means that the pass would either become language specific or environment specific because I would have to ensure that the source language compiler added the necessary constructs to the generated code to provide the contextual information needed by the pass. This raises a
couple questions more questions:

What you are talking about is very similar to what I would like to do : get structural information (list of functions, what do they call, lists of classes, etc). This means accessing and querying a program structure through an API. There was a post yesterday on LLVM/OpenC++, which is an attemp to offer C++ developers with an API to reflect a C++ program structure (they call it "meta-programming", but to me it seems mostly like reflectivity).

(1) Is there be a general mechanism to communicate information between source language compiler and LLVM pass? If there isn't, should there be? In the case I describe above it would be *highly* useful (IMO) to have the source language compiler provide source level information for a
language independent pass to use later.

I also think tt would be highly useful to have this information, at least for the following use cases :

* Add instrumentation operations at selected program points
* Allow program structural-analysis passes (which can detect for instance bad design in program structure, or optimise recurrent structural patterns)
* Introspect a program to automatically generate bindings to other language. Language X for LLVM could easily used libraries programmed in language Y for LLVM, because all program information is accessible, and has not to be generated using external utilites (like SWIG, for instance).

I must say that providing a compiler infrastructure that offers even basic reflectivity infrastructure for all front-ends would really ease the weaving of different programs. For instance, scripting languages like Python or Ruby (or anything dynamic) would have almost free bindings to any C or C++ library.

I hope this is possible, and am ready to help !

-- S├ębastien