diff --git a/doc/metadata_pt.html b/doc/metadata_pt.html
index fea69ff..04807a2 100644
--- a/doc/metadata_pt.html
+++ b/doc/metadata_pt.html
@@ -27,7 +27,7 @@ Path Tracing Details
in any object file linked into the executable. Each entry gives a
description of the control-flow graph of the function, augmented by line
number information and Ball/Larus instrumentation information (see
-[2 ]). A complete example
+[3 ]). A complete example
(from the unit test loop ) is:
#
@@ -215,10 +215,11 @@ Path Tracing Details
instrumentation and partial path reconstruction) and the edge
weight (for complete acyclic path reconstruction). If
the edge is a backedge, these numbers represent the reinitialization of the path
-sum. For details, see the paper
-[1 ], and the original
+sum. For details, see the papers
+[1 ,
+ 2 ], and the original
discussion of this instrumentation scheme in
-[2 ].
+[3 ].
In the example above, then, the basic block in function main
labeled 5 contains statements on lines 11, 12, and 13. It is not the end of
diff --git a/doc/references.html b/doc/references.html
index 619ead4..61569b8 100644
--- a/doc/references.html
+++ b/doc/references.html
@@ -8,6 +8,12 @@
+P. Ohmann and B. Liblit.
+Lightweight
+control-flow instrumentation and postmortem analysis in support of
+debugging . Automated Software Engineering Journal, 2016.
+Springer.
+
P. Ohmann and B. Liblit.
Lightweight
control-flow instrumentation and postmortem analysis in support of
diff --git a/doc/running.html b/doc/running.html
index 7d27e61..4e01631 100644
--- a/doc/running.html
+++ b/doc/running.html
@@ -30,6 +30,14 @@ Compiling with CSI
+To add instrumentation, you need to provide csi-cc with the set of
+possible tracing variants you desire for each function. The --trace
+option allows you to specify a file containing tracing schemes; otherwise,
+csi-cc will prompt you to provide the information via
+stdin . The tracing schemes page
+details the format for this data, and provides additional information.
+
+
Having problems? See some additional
comments .
diff --git a/doc/running_comments.html b/doc/running_comments.html
index 2c52057..4e50456 100644
--- a/doc/running_comments.html
+++ b/doc/running_comments.html
@@ -8,7 +8,7 @@
CSI Guide
@@ -29,50 +29,48 @@ Additional Comments and Suggestions
Error opening '/.../csi/Release/libCSI.so': libLLVM-3.X.so:
cannot open shared object file: No such file or directory
this means that the LLVM version you built with (see
-building notes on LLVMBIN)
+building notes on LLVM_CONFIG)
does not match the one in your PATH .
The full list of CSI-specific options is as follows
-USAGE: csi-cc [options] {inputs}
+USAGE: csi-cc [options] <inputs>
OPTIONS:
- -path-array-size {arg} Use {arg} as the size of path tracing arrays
+ --trace=<file> Use <file> as the input file for the tracing schemes.
+ If this option is not given, the scheme is read from
+ stdin.
+ -debug-pass=<arg> Enable printing of extremely verbose debug messages
+ for the pass specified as <arg>. This should
+ generally not be used unless debugging instrumentors.
+ <arg> can be any of the supported instrumentors or
+ 'all' (which enables debugging for all passes).
+ -path-array-size <arg> Use <arg> as the size of path tracing arrays
(Default: 10)
- -hash-size {arg} Use {arg} as the maximum-size function (in number of
+ -hash-size <arg> Use <arg> as the maximum-size function (in number of
acyclic paths) to instrument for path tracing
(Default: ULONG_MAX/2+1)
+ -no-filter Do not filter tracing schemes. All schemes provided
+ will be used verbatim for functions to which they
+ match.
--silent Do not print pass-specific warnings during
instrumentation
- --no-path-trace Do not instrument for path tracing
- --no-call-coverage Do not instrument for call coverage
--help Display this help message and exit
--help-clang Display additional options (clang's help message) and
exit
ENVIRONMENT VARIABLES:
- PT_INST_FUNCS A | separated list of functions for which path tracing
- should be initially enabled. The flag --no-path-trace
- (above) has precedence, and nullifies any information
- in this variable. If this environment variable does
- not exist, is empty, or is set to 'ALL', all functions
- are enabled.
-
PT_ARRAY_SIZE See -path-array-size (above). Flags have precedence.
PT_HASH_SIZE See -hash-size (above). Flags have precedence.
CSI_SILENT Enables or disables the printing of instrumentation
warnings.
See --silent (above). Flags have precedence.
- CSI_PT Enables or disables path tracing instrumentation.
- See --no-path-trace (above). Flags have precedence.
- CSI_CC Enables or disables call coverage instrumentation.
- See --no-call-coverage (above). Flags have precedence.
diff --git a/doc/running_schemes.html b/doc/running_schemes.html
new file mode 100644
index 0000000..d32f8d7
--- /dev/null
+++ b/doc/running_schemes.html
@@ -0,0 +1,147 @@
+
+
+
+
+
+Running CSI: Tracing Schemes
+
+
+CSI Guide
+
+
+Running CSI
+
+Tracing Schemes
+To compile with csi-cc , you must tell the compiler which tracing
+variants to create for each function. This page describes the methods for
+providing this data to csi-cc , and the format of the data.
+
+If not otherwise specified, csi-cc will prompt the user for
+tracing configuration (read from stdin ) before compilation.
+To specify a file containing appropriate tracing configuration, use the
+--trace flag. For example, to instrument every function with
+path tracing and call-site coverage, you can the pre-built file provided with
+this release, as follows:
+csi-cc --trace=$CSI_DIR/schemas/cc_pt.schema <input file>
+where $CSI_DIR refers to the root directory of this release.
+
+A description of tracing schemes and customization can be found in the
+journal paper associated with this code release
+[1 ]. In brief: each
+function in the original program has one or more possible sets of tracing
+mechanisms that it can have active during a run. Each such set is called a
+tracing “scheme.” For example, the aforementioned file
+cc_pt.schema encodes that all functions in the entire program have a
+single scheme: call-site coverage plus path tracing. Another provided example
+schema, realistic.schema , creates three different variants for each
+original function:
+
+No instrumentation: an exact copy of the original function
+Call-site coverage
+Call-site coverage plus path tracing
+
+
+A tracing schema is composed of an ordered list of matching rules. Each
+function in the program is instrumented based on the first matching rule. For
+example, the following schema:
+
+foo;{};{CC}
+*;{};{FC};{CC,PT}
+
+indicates that any function named foo will have two variants:
+
+No instrumentation
+Call-site coverage
+
+and that any other function will have three variants:
+
+No instrumentation
+Function coverage
+Call-site coverage plus path tracing
+
+
+The format grammar follows. Non-terminals are
+distinguished from terminals by capitalization, color,
+and italics.
+
+
+
+
+ Rule_List
+ ⩴
+ Rule ↵ Rule_List
+
+
+
+ |
+ ε
+
+
+
+
+ Rule
+ ⩴
+ fn-name ; Scheme_List
+
+
+
+ |
+ * ; Scheme_List
+
+
+
+
+ Scheme_List
+ ⩴
+ Scheme ; Scheme_List
+
+
+
+ |
+ Scheme
+
+
+
+
+ Scheme
+ ⩴
+ { Mechanism_List }
+
+
+
+ |
+ { }
+
+
+
+
+ Mechanism_List
+ ⩴
+ Mechanism , Mechanism_List
+
+
+
+ |
+ Mechanism
+
+
+
+
+ Mechanism
+ ⩴
+ BBC | CC | FC | PT
+
+
+
+
+
+
+
+
+
diff --git a/doc/variables.html b/doc/variables.html
index bce49a2..45ba3e8 100644
--- a/doc/variables.html
+++ b/doc/variables.html
@@ -19,41 +19,39 @@ General
running program. It is not written out to an external trace file. To extract
this information, you will need a debugger, such as
gdb . For further details, please
-see [1 ].
+see
+[1 ,
+ 2 ].
Path Tracing
Path Tracing data is always stack-bound. The same variable names are used
for all functions.
-The local variable __PT_pathArr
exists for each instrumented
-function in the program stack. If tracing is enabled at run time for this
-function, then __PT_pathArr
is an array holding the
-last N acyclic paths taken in the particular invocation of the
-function. The size of the array is defined when compiling
-with csi-cc (see comments on
-running csi-cc ), and defaults to 10. The last value of
-__PT_pathArr
is particularly important: if the last value is -1,
-then fewer than N acyclic paths were completed; otherwise, the
-circular array has wrapped around at least once. If tracing is disabled, array
-entries are not meaningful.
-
-The local variable __PT_arrIndex
exists for each instrumented
-function in the program stack. If tracing is enabled at run time for this
-function, then __PT_arrIndex
contains the index
-in __PT_pathArr
for the next acyclic path entry.
-For example, if the value of __PT_arrIndex
is 4, then the last
-completed acyclic path is stored in
-__PT_pathArr[3]
. If tracing is disabled,
-then __PT_arrIndex
has the value -1.
-
-The local variable __PT_curPath
exists for each instrumented
-function in the program stack. If tracing is enabled at run time for this
-function, then __PT_curPath
holds the current path sum. Changes in
-the value of this variable are expounded in the Path
-Tracing metadata page. This value is reset to 0 whenever an acyclic path is
-completed and written into
-__PT_pathArr[__PT_arrIndex]
, and intialized along backedges. If
-tracing is disabled, this value is not meaningful.
+The local variable __PT_pathArr
exists for each function variant
+instrumented for path tracing. While the function is executing (i.e., on the
+active program stack) __PT_pathArr
is an array holding the last
+N acyclic paths taken in the particular invocation of the function.
+The size of the array is defined when compiling with csi-cc (see comments on running csi-cc ), and
+defaults to 10. The last value of __PT_pathArr
is particularly
+important: if the last value is -1, then fewer than N acyclic paths
+were completed; otherwise, the circular array has wrapped around at least
+once.
+
+The local variable __PT_arrIndex
exists for each function
+variant instrumented for path tracing. While the function is executing (i.e.,
+on the active program stack) __PT_arrIndex
contains the index in
+__PT_pathArr
for the next acyclic path entry. For
+example, if the value of __PT_arrIndex
is 4, then the last
+completed acyclic path is stored in __PT_pathArr[3]
.
+
+The local variable __PT_curPath
exists for each function variant
+instrumented for path tracing. While the function is executing (i.e., on the
+active program stack) __PT_curPath
holds the current path sum.
+Changes in the value of this variable are expounded in the Path Tracing metadata page. This value is reset to
+0 whenever an acyclic path is completed and written into
+__PT_pathArr[__PT_arrIndex]
, and intialized along backedges.
Examples
@@ -87,60 +85,132 @@ Examples
-
-
Customization
-
Tracing of individual functions can be enabled and disabled via very simple
-binary modification of instrumented executables. Each individual instrumented
-function has a unique global flag appearing in the __PT_func_inst
-section of the data segment. The name for each flag is the concatenation of
-__PT_inst_ , a C-legal string representing the compilation unit, and
-the function name. For example, a function foo
in file
-bar.c
might have a name something like
-__PT_inst_bar_c_foo .
-
-
-Call Coverage
-Call Coverage data for each function is stored in one local and one global
-array, both of the same size. The arrays for local coverage information share
-the same name across different functions, and global coverage shares one array
-across all invocations of each function.
+Program Coverage
-The local variable __CC_arr
exists for each instrumented
-function in the program stack. The size of this array is determined by the
-number of call sites in the function. Each entry will be either true
-(non-zero) or false (zero), indicating whether or not the call site was
-executed (so far) in the current invocation of the function.
+Program Coverage data for each function (with the corresponding coverage
+instrumentation) is stored in one local and one global array, both of the same
+size. The arrays for local coverage information share the same name across
+different functions, and global coverage shares one array across all invocations
+of each function. Note that function coverage is a special case: there is no
+local coverage array for function coverage (as all functions on the active stack
+are clearly already executing), and global arrays for function coverage are
+always of size 1.
-Each instrumented function, with name f
, also has a
-global array with name __CC_arr_f
. Again, the size of
+
Local variables exist for each instrumented function variant, and are
+accessible from the active program stack. The following local variables hold
+stack-local information for call-site coverage and statement coverage,
+respectively.
+
+__CC_arr
is the local variable for call-site coverage data, and
+exists for all function variants with call-site coverage enabled. The size of
this array is determined by the number of call sites in the function. Each
-entry will be either
-true (non-zero) or false (zero), indicating whether or not
-any invocation of this call site was ever executed (so far)
-across the execution of the entire program.
+entry will be either true (non-zero) or false (zero),
+indicating whether or not the call site was executed (so far) in the current
+invocation of the function.
+__BBC_arr
is the local variable for statement coverage data,
+and exists for all function variants with statement coverage enabled. The size
+of this array is determined by the number of basic blocks in the function. Each
+entry will be either true (non-zero) or false (zero),
+indicating whether or not the indicated basic block has completed execution at
+least once (so far) in the current invocation of the function.
+
+
+Each instrumented function, with name f
, also has up
+to 3 global arrays for coverage data (one for each of function, call-site, and
+statement coverage data; depending on the instrumentation schemes available for
+that function). The specific names of these global arrays are given in the
+metadata for each type of program coverage (see the
+
+metadata page for details). Sample array data
+(for the same example) might be:
+
+A global variable __FC_arr_f
for function coverage.
+This is a single Boolean variable. Its value will be either true
+(non-zero) or false (zero), indicating whether or not any portion of
+f
was ever executed (so far) across the execution of the
+entire program.
+A global variable __CC_arr_f
for call-site coverage.
+The size of this array is determined by the number of call sites in the
+function. Each entry will be either true (non-zero) or false
+(zero), indicating whether or not any invocation of this call
+site was ever executed (so far) across the execution of the entire program.
+A global variable __BBC_arr_f
for statement
+coverage. The size of this array is determined by the number of basic blocks in
+the function. Each entry will be either true (non-zero) or
+false (zero), indicating whether or not the indicated basic block has
+completed execution (so far) during any invocation of the
+function across the execution of the entire program.
+
Example
This example references the metadata from the example on the
-Call Coverage metadata page. The example is
+Program Coverage metadata page. The example is
given from the perspective of currently executing function a
at
line 18, called by function b
at line 26, called by function
main
at line 36.
+__FC_arr_abc
= 1
+__FC_arr_a
= 1
+__FC_arr_b
= 1
+__FC_arr_main
= 1
+
__CC_arr_a
= {1, 1, 1, 1, 1}
__CC_arr_b
= {0}
__CC_arr_main
= {1, 1, 0, 0}
-__CC_arr
= {0, 0, 1, 0, 0}
-
In this example, the current invocation of a
has only completed
-the call to abc
at line 17 (denoted by __CC_arr[2]
).
-However, previous invocations of a
have executed the other four
-call sites in a
at lines 14, 18, 19, and 20 (as indicated by
+
+__BBC_arr_abc
= {1, 0, 1, 1}
+__BBC_arr_a
= {1, 1, 1, 1}
+__BBC_arr_b
= {1, 0, 0, 0}
+__BBC_arr_main
= {0}
+
+__CC_arr
= {0, 0, 1, 0, 0}
+__BBC_arr
= {1, 1, 0, 0}
+
+
First, consider the function coverage variables for each function. For this
+example, we obtain very little information: since functions a
,
+b
, and main
are in the active stack, their respective
+values are known to be true (as they are set at function entry). We
+can, however, see that function abc
previously executed.
+
+
Next, consider the call-site coverage variables. In this example, the
+current invocation of a
has only completed the call to
+abc
at line 17 (denoted by __CC_arr[2]
). However,
+previous invocations of a
have executed the other four call sites
+in a
at lines 14, 18, 19, and 20 (as indicated by
__CC_arr_a
). The next stack frame (not shown) would indicate that
execution in function b
is currently executing, but has not yet
returned from, the call to a
at line 26 (as indicated by
__CC_arr_b[0]
).
+
+
Finally, consider the statement coverage variables. The local variable again
+indicates the same information as __CC_arr
: the basic block
+containing function entry plus lines 12 and 13 has executed (denoted by
+__BBC_arr[0]
); further, the basic block containing line 14 has
+executed (denoted by __BBC_arr[1]
). Note that
+__BBC_arr[3]
indicates that this invocation of a
has
+not yet executed the block containing lines 17, 18, 19, 20, and 21. This is
+true: that block has only partially executed. One can glean similar
+information from the global statement coverage arrays. Function
+abc
executed previously, but on no previous execution was the
+return statement on line 5 executed (as indicated by
+__BBC_arr_abc[1]
). In addition to the fact that this is the first
+call to a
via line 26 in function b
(which is visible
+from __CC_arr_b[0]
), __BBC_arr_b[2]
indicates that no
+previous call to b
executed the return statement on line 27.
+
+
← Prev
diff --git a/driver/SConscript b/driver/SConscript
index 113808f..f37caba 100644
--- a/driver/SConscript
+++ b/driver/SConscript
@@ -1,8 +1,2 @@
-from os import path
-
-Import('env')
-
-lenv = env.Clone()
-
installed = InstallAs(target="#Release/csi-cc", source="csi-driver.py")
Default(installed)
diff --git a/driver/csi-driver.py b/driver/csi-driver.py
index ab2d30e..c22b37b 100755
--- a/driver/csi-driver.py
+++ b/driver/csi-driver.py
@@ -7,7 +7,7 @@
# instrumenting compilation.
#====----------------------------------------------------------------------====#
#
-# Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+# Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -33,12 +33,13 @@
PATH_TO_CSI_DRIVER = os.path.join(PATH_TO_CSI, "driver")
path.insert(1, PATH_TO_CSI_DRIVER)
-from driver import Driver, drive
+from driver import Driver, drive, regexpHandlerTable
class CSIDriver(Driver):
__slots__ = "__pathArraySize", "__hashSize", "__silent",\
- "__doPaths", "__doCalls",\
- "__trackerFile", "__ccFile"
+ "__ptFile", "__ccFile", "__bbcFile",\
+ "__fcFile", "__traceFile",\
+ "__debugPass", "__filter"
__pychecker__ = 'unusednames=_flag'
@@ -51,35 +52,48 @@ def __handleHashSize(self, _flag, _value):
def __handleSilent(self, _flag):
self.__silent = True
- def __handleNoPathTrace(self, _flag):
- self.__doPaths = False
-
- def __handleNoCallCoverage(self, _flag):
- self.__doCalls = False
-
def __handleFlagGoalHelpCSI(self, _flag):
self.finalGoal = self.__buildCSIHelp
def __handleFlagGoalHelpClang(self, _flag):
return super(CSIDriver, self).handleFlagGoalHelp('--help')
+
+ def __handleNoFilter(self, _flag):
+ self.__filter = False
+
+ def __handleSpecificTrace(self, _flag):
+ if not os.path.exists(_flag[8:].strip()):
+ print >> stderr, "ERROR: trace file does not exist. Revise --trace argument."
+ exit(1)
+ else:
+ self.__traceFile = _flag[8:].strip()
+
+ def __handleDebugPass(self, _flag):
+ self.__debugPass = _flag[12:].strip().lower()
EXTRA_EXACT_HANDLERS = {
"-path-array-size" : __handlePathArraySize,
"-hash-size" : __handleHashSize,
+ "-no-filter" : __handleNoFilter,
"--silent" : __handleSilent,
- "--no-path-trace" : __handleNoPathTrace,
- "--no-call-coverage" : __handleNoCallCoverage,
"--help" : __handleFlagGoalHelpCSI,
- "--help-clang" : __handleFlagGoalHelpClang,
+ "--help-clang" : __handleFlagGoalHelpClang
}
+ EXTRA_REGEXP_HANDLERS = regexpHandlerTable(
+ ('^(--trace=.+)$', __handleSpecificTrace),
+ ('^(-debug-pass=.+)$', __handleDebugPass),
+ )
+
def __init__(self):
- Driver.__init__(self, extraExact=self.EXTRA_EXACT_HANDLERS)
+ Driver.__init__(self, extraExact=self.EXTRA_EXACT_HANDLERS,
+ extraRegexp=self.EXTRA_REGEXP_HANDLERS)
self.__pathArraySize = environ.get("PT_ARRAY_SIZE", "")
self.__hashSize = environ.get("PT_HASH_SIZE", "")
self.__silent = strtobool(environ.get("CSI_SILENT", "0").strip())
- self.__doPaths = strtobool(environ.get("CSI_PT", "1").strip())
- self.__doCalls = strtobool(environ.get("CSI_CC", "1").strip())
+ self.__filter = True
+ self.__traceFile = None
+ self.__debugPass = ""
def process(self, args):
# instrumentation *requires* debug information
@@ -98,35 +112,72 @@ def __checkPositiveInt(self, setting, flag, description):
print >>stderr, 'WARNING: %s must be a positive integer; ignoring "%s"' % (description, setting)
def getExtraOptArgs(self):
-
+ if self.__debugPass == "all":
+ yield "--debug-pass=Structure"
+ yield "-debug"
+ elif self.__debugPass == "prep":
+ yield "-debug-only=csi-prep"
+ elif len(self.__debugPass.strip()) > 0:
+ yield "-debug-only=" + self.__debugPass.strip()
+
# instrumentation plug-in
yield "-load"
yield os.path.join(PATH_TO_CSI_RELEASE, "libCSI.so")
-
+ yield "-csi"
+ if self.__traceFile:
+ yield "-csi-variants-file"
+ yield self.__traceFile
+ if not self.__filter:
+ yield "-csi-no-filter"
+ if self.__silent:
+ yield "-csi-silent"
+
# path tracing instrumentation
- if self.__doPaths:
- yield "-pt-inst"
- yield "-pt-tracker-file"
- yield self.__trackerFile
- for arg in self.__checkPositiveInt(self.__pathArraySize, '-pt-path-array-size', 'path tracing array size'):
- yield arg
- for arg in self.__checkPositiveInt(self.__hashSize, '-pt-hash-size', 'path count "hash" size'):
- yield arg
- if self.__silent:
- yield "-pt-silent"
+ yield "-pt-inst"
+ yield "-pt-info-file"
+ yield self.__ptFile
+ for arg in self.__checkPositiveInt(self.__pathArraySize, '-pt-path-array-size', 'path tracing array size'):
+ yield arg
+ for arg in self.__checkPositiveInt(self.__hashSize, '-pt-hash-size', 'path count "hash" size'):
+ yield arg
+ if self.__silent:
+ yield "-pt-silent"
+ if self.__debugPass == "pt":
+ yield "-debug-only=path-tracing"
# call coverage instrumentation
- if(self.__doCalls):
- yield "-call-coverage"
- yield "-cc-info-file"
- yield self.__ccFile
+ yield "-call-coverage"
+ yield "-cc-info-file"
+ yield self.__ccFile
if(self.__silent):
yield "-cc-silent"
-
+ if self.__debugPass == "cc":
+ yield "-debug-only=call-coverage"
+
+ # basic block coverage instrumentation
+ yield "-bb-coverage"
+ yield "-bbc-info-file"
+ yield self.__bbcFile
+ if(self.__silent):
+ yield "-bbc-silent"
+ if self.__debugPass == "bbc":
+ yield "-debug-only=bb-coverage"
+
+ # function coverage instrumentation
+ yield "-fn-coverage"
+ yield "-fc-info-file"
+ yield self.__fcFile
+ if(self.__silent):
+ yield "-fc-silent"
+ if self.__debugPass == "fc":
+ yield "-debug-only=func-coverage"
+
def instrumentBitcode(self, inputFile, uninstrumented, instrumented):
- # output files for static data
- self.__trackerFile = self.temporaryFile(inputFile, ".tracker.info")
+ # output files/directories for static/temporary data
+ self.__ptFile = self.temporaryFile(inputFile, ".pt.info")
self.__ccFile = self.temporaryFile(inputFile, ".cc.info")
+ self.__bbcFile = self.temporaryFile(inputFile, ".bbc.info")
+ self.__fcFile = self.temporaryFile(inputFile, ".fc.info")
super(CSIDriver, self).instrumentBitcode(inputFile, uninstrumented, instrumented)
@@ -137,8 +188,10 @@ def __embedSection(self, objectFile, section, filename):
def compileTo(self, inputFile, objectFile, args, targetFlag):
super(CSIDriver, self).compileTo(inputFile, objectFile, args, targetFlag)
- self.__embedSection(objectFile, 'PT', self.__trackerFile)
+ self.__embedSection(objectFile, 'PT', self.__ptFile)
self.__embedSection(objectFile, 'CC', self.__ccFile)
+ self.__embedSection(objectFile, 'BBC', self.__bbcFile)
+ self.__embedSection(objectFile, 'FC', self.__fcFile)
def __buildCSIHelp(self, args):
__pychecker__ = 'unusednames=args'
@@ -149,37 +202,35 @@ def __buildCSIHelp(self, args):
USAGE: csi-cc [options]
OPTIONS:
+ --trace= Use as the input file for the tracing schemes.
+ If this option is not given, the scheme is read from
+ stdin.
+ -debug-pass= Enable printing of extremely verbose debug messages
+ for the pass specified as . This should
+ generally not be used unless debugging instrumentors.
+ can be any of the supported instrumentors or
+ 'all' (which enables debugging for all passes).
-path-array-size Use as the size of path tracing arrays
(Default: 10)
-hash-size Use as the maximum-size function (in number of
acyclic paths) to instrument for path tracing
(Default: ULONG_MAX/2+1)
+ -no-filter Do not filter tracing schemes. All schemes provided
+ will be used verbatim for functions to which they
+ match.
--silent Do not print pass-specific warnings during
instrumentation
- --no-path-trace Do not instrument for path tracing
- --no-call-coverage Do not instrument for call coverage
--help Display this help message and exit
--help-clang Display additional options (clang's help message) and
exit
ENVIRONMENT VARIABLES:
- PT_INST_FUNCS A | separated list of functions for which path tracing
- should be initially enabled. The flag --no-path-trace
- (above) has precedence, and nullifies any information
- in this variable. If this environment variable does
- not exist, is empty, or is set to 'ALL', all functions
- are enabled.
-
PT_ARRAY_SIZE See -path-array-size (above). Flags have precedence.
PT_HASH_SIZE See -hash-size (above). Flags have precedence.
CSI_SILENT Enables or disables the printing of instrumentation
warnings.
See --silent (above). Flags have precedence.
- CSI_PT Enables or disables path tracing instrumentation.
- See --no-path-trace (above). Flags have precedence.
- CSI_CC Enables or disables call coverage instrumentation.
- See --no-call-coverage (above). Flags have precedence.
"""
def main():
diff --git a/driver/driver.py b/driver/driver.py
index e4adb82..f12b370 100644
--- a/driver/driver.py
+++ b/driver/driver.py
@@ -6,7 +6,7 @@
# instrumenting compilation. It must be subclassed.
#====----------------------------------------------------------------------====#
#
-# Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+# Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -29,13 +29,14 @@
from errno import ENOENT
from inspect import getargspec
from itertools import chain, imap, starmap
-from os import remove
+from os import environ, remove, mkdir
from os.path import basename, splitext
from pipes import quote
from shlex import split
from subprocess import CalledProcessError, check_call
from sys import argv, stderr
-from tempfile import NamedTemporaryFile
+from tempfile import NamedTemporaryFile, mkdtemp
+from shutil import rmtree
########################################################################
@@ -215,6 +216,7 @@ class Driver(object):
'__verbose',
'finalGoal',
'temporaryFile',
+ 'temporaryDir',
)
def __init__(self, extraExact=dict(), extraRegexp=tuple()):
@@ -230,6 +232,7 @@ def __init__(self, extraExact=dict(), extraRegexp=tuple()):
self.__verbose = False
self.finalGoal = self.__buildLinked
self.temporaryFile = self.__namedTemporaryFile
+ self.temporaryDir = self.__namedTemporaryDir
####################################################################
#
@@ -259,12 +262,14 @@ def __handleFlagGoalObject(self, _flag):
def __handleFlagSaveTempsCwd(self, flag):
"""handle "-save-temps" and "-save-temps=cwd" flags"""
self.temporaryFile = self.derivedFile
+ self.temporaryDir = self.derivedDir
self.__derivedFilesNearOutputFile = False
return self.__handleFlagOptionNoValue(flag)
def __handleFlagSaveTempsObj(self, flag):
"""handle "-save-temps=obj" flag"""
self.temporaryFile = self.derivedFile
+ self.temporaryDir = self.derivedDir
self.__derivedFilesNearOutputFile = True
return self.__handleFlagOptionNoValue(flag)
@@ -500,6 +505,15 @@ def derivedFile(self, inputFile, extension):
basis = self.__outputFile if basedOnOutput else basename(inputFile.filename)
return splitext(basis)[0] + extension
+ def derivedDir(self, inputFile, extension):
+ """create a (non-temporary) directory based on some other file name, but with a new extension"""
+ basedOnOutput = self.__derivedFilesNearOutputFile and self.__outputFile
+ basis = self.__outputFile if basedOnOutput else basename(inputFile.filename)
+ dirName = splitext(basis)[0] + extension
+ rmtree(dirName, ignore_errors=True)
+ mkdir(dirName)
+ return dirName
+
@staticmethod
def __namedTemporaryFile(_inputFile, extension):
"""create a temporary file with a given extension"""
@@ -509,6 +523,14 @@ def __namedTemporaryFile(_inputFile, extension):
register(Driver.__tryRemove, handle.name)
return handle.name
+ @staticmethod
+ def __namedTemporaryDir(_inputFile, extension):
+ """create a temporary directory"""
+ __pychecker__ = 'unusednames=_inputFile'
+ tmpDir = mkdtemp(prefix='lid-', suffix=extension)
+ register(Driver.__tryRemoveDir, tmpDir)
+ return tmpDir
+
@staticmethod
def __tryRemove(chaff):
"""Remove a file that may already be gone."""
@@ -518,6 +540,15 @@ def __tryRemove(chaff):
if error.errno != ENOENT:
raise error
+ @staticmethod
+ def __tryRemoveDir(chaff):
+ """Remove a directory that may already be gone."""
+ try:
+ rmtree(chaff)
+ except OSError, error:
+ if error.errno != ENOENT:
+ raise error
+
####################################################################
#
# compilation helpers
@@ -541,9 +572,10 @@ def instrumentBitcode(self, inputFile, uninstrumented, instrumented):
"""add instrumentation to a single source file's bitcode"""
# pylint: disable=W0613
__pychecker__ = 'unusednames=inputFile'
+ wrapper = split(environ.get('OPT_WRAPPER', ''), comments=False)
prelude = ('opt', '-o', instrumented, uninstrumented)
phases = self.getExtraOptArgs()
- command = chain(prelude, phases)
+ command = chain(wrapper, prelude, phases)
self.run(command)
def getExtraOptArgs(self):
diff --git a/instrumentor/.gitignore b/instrumentor/.gitignore
new file mode 100644
index 0000000..7808772
--- /dev/null
+++ b/instrumentor/.gitignore
@@ -0,0 +1,2 @@
+*.os
+*.pyc
diff --git a/instrumentor/BBCoverage.cpp b/instrumentor/BBCoverage.cpp
new file mode 100644
index 0000000..9a8633d
--- /dev/null
+++ b/instrumentor/BBCoverage.cpp
@@ -0,0 +1,128 @@
+//===--------------------------- BBCoverage.h -----------------------------===//
+//
+// This pass instruments basic blocks for interprocedural analysis by
+// gathering both global and local coverage information.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "bb-coverage"
+
+#include "BBCoverage.h"
+#include "CoveragePassNames.h"
+#include "Utils.hpp"
+
+#include
+#include
+
+#include "llvm_proxy/InstIterator.h"
+#include "llvm_proxy/IntrinsicInst.h"
+#include "llvm_proxy/Module.h"
+
+using namespace csi_inst;
+using namespace llvm;
+using namespace std;
+
+
+const CoveragePassNames BBCoverage::names = {
+ "bbc",
+ "BBC",
+ "basic block",
+ "Basic Block",
+};
+
+BBCoverage::Options BBCoverage::options(
+ names
+);
+
+// Register basic block coverage as a pass
+char BBCoverage::ID = 0;
+static RegisterPass X("bb-coverage",
+ "Insert basic block coverage instrumentation",
+ false, false);
+
+
+// No longer requires any additional LLVM passes
+void BBCoverage::getAnalysisUsage(AnalysisUsage &AU) const {
+ LocalCoveragePass::getAnalysisUsage(AU);
+}
+
+
+void BBCoverage::writeOneBB(BasicBlock* theBlock, unsigned int index,
+ bool isInstrumented){
+ infoStream << (isInstrumented?"":"-") << index;
+
+ bool printedOne = false;
+ for(BasicBlock::iterator i = theBlock->begin(), e = theBlock->end(); i != e; ++i){
+ DebugLoc dbLoc = (&*i)->getDebugLoc();
+ if(dbLoc.isUnknown())
+ continue;
+
+ infoStream << "|" << dbLoc.getLine();
+ printedOne = true;
+ }
+
+ if(!printedOne)
+ infoStream << "|NULL";
+
+ infoStream << endl;
+}
+
+
+void BBCoverage::instrumentFunction(Function &function)
+{
+ // get [optimized] basic blocks to cover in function
+ set fBBs;
+ for (Function::iterator i = function.begin(), e = function.end(); i != e; ++i)
+ fBBs.insert(i);
+ unsigned int arraySize = fBBs.size();
+ if (arraySize < 1)
+ return;
+
+ BasicBlock &entryBlock = function.getEntryBlock();
+ BasicBlock::iterator entryInst = entryBlock.getFirstInsertionPt();
+
+ const CoverageArrays arrays = prepareFunction(function, arraySize, options.silentInternal);
+
+ // instrument each site
+ unsigned int curIdx = 0;
+ for (set::iterator i = fBBs.begin(), e = fBBs.end(); i != e; ++i)
+ {
+ BasicBlock &block = **i;
+
+ // find a suitable insertion point; if the entry basic block, we
+ // need to be after the array declaration
+ IRBuilder<> builder(&block, &block == &entryBlock ? entryInst : block.getFirstInsertionPt());
+
+ // add instrumentation to set local and global coverage bits
+ insertArrayStoreInsts(arrays, curIdx, builder);
+
+ // write out the instrumentation site's static location details
+ writeOneBB(&block, curIdx++, true);
+ }
+
+ // write out uninstrumented sites
+ unsigned int uninstIdx = 1;
+ for (Function::iterator i = function.begin(), e = function.end(); i != e; ++i)
+ if (!fBBs.count(i))
+ writeOneBB(i, uninstIdx++, false);
+}
+
+
+bool BBCoverage::runOnModule(Module &module){
+ static bool runBefore;
+ return runOnModuleOnce(module, options.infoFile, runBefore);
+}
diff --git a/instrumentor/BBCoverage.h b/instrumentor/BBCoverage.h
new file mode 100644
index 0000000..82eaea9
--- /dev/null
+++ b/instrumentor/BBCoverage.h
@@ -0,0 +1,63 @@
+//===--------------------------- BBCoverage.h -----------------------------===//
+//
+// This pass instruments basic blocks for interprocedural analysis by
+// gathering both global and local coverage information.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_BB_COVERAGE_H
+#define CSI_BB_COVERAGE_H
+
+#include "LocalCoveragePass.h"
+
+#include "llvm_proxy/CFG.h"
+#include "llvm_proxy/DebugInfo.h"
+#include "llvm_proxy/Instructions.h"
+
+namespace csi_inst {
+
+// ---------------------------------------------------------------------------
+// BBCoverage is a module pass which does basic block coverage instrumentation
+// ---------------------------------------------------------------------------
+class BBCoverage : public LocalCoveragePass {
+private:
+ static Options options;
+
+ // Perform module-level tasks, open streams, and instrument each function
+ bool runOnModule(llvm::Module &M);
+
+ // Write out the information for one basic block within a function
+ void writeOneBB(llvm::BasicBlock* theBB, unsigned int index,
+ bool isInstrumented=true);
+
+ // Instrument each function with coverage for each basic block
+ void instrumentFunction(llvm::Function &);
+
+public:
+ static const CoveragePassNames names;
+ static char ID; // Pass identification, replacement for typeid
+ BBCoverage() : LocalCoveragePass(ID, names) {}
+
+ virtual const char *getPassName() const {
+ return "Intra/Interprocedural Basic Block Coverage Instrumentation";
+ }
+
+ void getAnalysisUsage(llvm::AnalysisUsage &) const;
+};
+} // end csi_inst namespace
+
+#endif
diff --git a/instrumentor/CallCoverage.cpp b/instrumentor/CallCoverage.cpp
index 622e278..9646a02 100644
--- a/instrumentor/CallCoverage.cpp
+++ b/instrumentor/CallCoverage.cpp
@@ -5,7 +5,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,28 +22,32 @@
#define DEBUG_TYPE "call-coverage"
#include "CallCoverage.h"
+#include "CoveragePassNames.h"
+#include "ExtrinsicCalls.h"
#include "Utils.hpp"
#include
-#include
#include
-#include
+#include "llvm_proxy/InstIterator.h"
#include "llvm_proxy/IntrinsicInst.h"
#include "llvm_proxy/Module.h"
-#include
-
using namespace csi_inst;
using namespace llvm;
using namespace std;
-static cl::opt SilentInternal("cc-silent", cl::desc("Silence internal "
- "warnings. Will still print errors which "
- "cause CC to fail."));
-static cl::opt InfoFile("cc-info-file", cl::desc("The path to "
- "the call coverage info file."),
- cl::value_desc("file_path"));
+
+const CoveragePassNames CallCoverage::names = {
+ "cc",
+ "CC",
+ "call",
+ "Call",
+};
+
+CallCoverage::Options CallCoverage::options(
+ names
+);
// Register call coverage as a pass
char CallCoverage::ID = 0;
@@ -51,263 +55,61 @@ static RegisterPass X("call-coverage",
"Insert call coverage instrumentation",
false, false);
-void CallCoverage::writeFunctionValue(Function& F){
- _infoStream << "#" << F.getName().str() << "|"
- << funcToGlobalArray[&F]->getName().str() << endl;
-}
-void CallCoverage::writeOneCall(CallInst* theCall, unsigned int index){
+void CallCoverage::writeOneCall(CallInst* theCall, unsigned int index,
+ bool isInstrumented){
unsigned int lineNum = 0;
DebugLoc dbLoc = theCall->getDebugLoc();
if(!dbLoc.isUnknown())
lineNum = dbLoc.getLine();
- string fnName = "?";
Function* calledFn = theCall->getCalledFunction();
- if(calledFn)
- fnName = calledFn->getName();
- _infoStream << index << "|" << lineNum << "|" << fnName << endl;
+ const string fnName = calledFn ? calledFn->getName() : "?";
+ infoStream << (isInstrumented ? "" : "-") << index << '|' << lineNum << '|'
+ << fnName << '\n';
}
-// ***
-// runOnFunction: Entry point of the function
-// ***
-bool CallCoverage::runOnFunction(Function& F) {
- DEBUG(dbgs() << "Function: " << F.getName() << "\n");
-
- set myCalls = functionCalls[&F];
- unsigned int arraySize = myCalls.size();
-
- PathTracing* pathsPass = getAnalysisIfAvailable();
- map oldToNewMap;
- if(pathsPass)
- oldToNewMap = pathsPass->oldToNewCallMap;
- else{
- for (set::iterator i = myCalls.begin(), e = myCalls.end(); i != e; ++i){
- oldToNewMap[*i] = NULL;
- }
- }
-
- if(arraySize != oldToNewMap.size()){
- cerr << "ERROR: Call-site count mismatch for Path and Coverage passes "
- << "for instrumented function '" + F.getName().str() + "'. Values "
- << "were " << arraySize << " and " << oldToNewMap.size() << ". This "
- << "is a tool error." << endl;
- exit(1);
- }
- else if(arraySize < 1)
- return false;
-
- Type* tBool = Type::getInt8Ty(*Context);
- Type* tInt = Type::getInt32Ty(*Context);
- Type* tArr = ArrayType::get(tBool, arraySize);
-
- // grab the global coverage array
- GlobalVariable* arrGlobal = funcToGlobalArray[&F];
-
- // declare the local coverage array
- Instruction* entryInst = F.getEntryBlock().getFirstNonPHI();
- AllocaInst* arrInst = new AllocaInst(tArr, "__CC_arr", entryInst);
- std::vector gepIndices(2);
- gepIndices[0] = Constant::getNullValue(Type::getInt32Ty(*Context));
- for(unsigned int i = 0; i < arraySize; i++){
- gepIndices[1] = ConstantInt::get(tInt, i);
- GetElementPtrInst* anEntry = GetElementPtrInst::Create(arrInst, gepIndices,
- "initArray", entryInst);
- new StoreInst(ConstantInt::get(tBool, 0), anEntry, true, entryInst);
- }
-
- // get the debug location of any instruction in the entry
- // block--this will use the same info. If there is none,
- // technically we should build it, but that's a pain in
- // the ass (if it's possible) so I just give up right now
- BasicBlock* entryBB = F.getEntryBlock().getFirstNonPHI()->getParent();
- DebugLoc dbLoc;
- for(BasicBlock::iterator i = entryBB->begin(), e = entryBB->end(); i != e; ++i){
- dbLoc = i->getDebugLoc();
- if(!dbLoc.isUnknown())
- break;
- }
- if(dbLoc.isUnknown()){
- // try again iterating through the entire function...
- for(inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i){
- dbLoc = i->getDebugLoc();
- if(!dbLoc.isUnknown())
- break;
- }
-
- if(dbLoc.isUnknown() && !SilentInternal){
- errs() << "WARNING: there will be no debug locations for instrumented"
- << " function "+F.getName()+" . Call coverage will likely "
- << "be unextractable!\n";
- }
- else if(!SilentInternal){
- DEBUG(dbgs() << "WARNING: debug location outside of entry block used "
- << "for instrumented function "+F.getName()+"\n");
- }
- }
-
- // give it debug info
- if(!dbLoc.isUnknown()){
- DIType boolType = Builder->createBasicType("__cc_bool", 8, 8, dwarf::DW_ATE_boolean);
- Value* subscript = Builder->getOrCreateSubrange(0, arraySize-1);
- DIArray subscriptArray = Builder->getOrCreateArray(subscript);
- DIType arrType = Builder->createArrayType(arraySize*8, 8, boolType,
- subscriptArray);
- DIVariable arrDI = Builder->createLocalVariable(
- (unsigned)dwarf::DW_TAG_auto_variable,
- DIDescriptor(dbLoc.getScope(*Context)),
- "__CC_arr",
- DIFile(dbLoc.getScope(*Context)), 0, arrType);
- Instruction* declareArr = Builder->insertDeclare(arrInst, arrDI, entryInst);
- declareArr->setDebugLoc(dbLoc);
- }
-
- // write out the function name and its arrays
- writeFunctionValue(F);
+void CallCoverage::instrumentFunction(Function &function)
+{
+ // find all of the callsites in function
+ set fCalls;
+ const ExtrinsicCalls calls = extrinsicCalls(function);
+ for (ExtrinsicCalls::iterator call = calls.begin(); call != calls.end(); ++call)
+ fCalls.insert(call);
+
+ // make globals and do instrumentation for each function
+ unsigned int arraySize = fCalls.size();
+ if (arraySize < 1)
+ return;
+
+ const CoverageArrays arrays = prepareFunction(function,
+ arraySize,
+ options.silentInternal);
- // instrument each call instruction
+ // instrument each site
unsigned int curIdx = 0;
- for(map::iterator i = oldToNewMap.begin(), e = oldToNewMap.end(); i != e; ++i){
- CallInst* oldCall = dyn_cast(i->first);
- if(!oldCall || (oldCall->getCalledFunction() &&
- oldCall->getCalledFunction()->isIntrinsic()))
- continue;
- // if the body was not replicated, second will be NULL
- CallInst* newCall = (i->second == NULL) ?
- NULL : dyn_cast(i->second);
-
- if(!myCalls.count(oldCall) || (newCall != NULL && !myCalls.count(newCall))){
- cerr << "ERROR: set of calls does not match for instrumentation passes "
- << "for function " << F.getName().str() << endl;
- exit(1);
- }
-
- Instruction* afterOld = nextInst(oldCall);
- gepIndices[0] = Constant::getNullValue(Type::getInt32Ty(*Context));
- gepIndices[1] = ConstantInt::get(tInt, curIdx);
- GetElementPtrInst* oldGEP = GetElementPtrInst::Create(arrInst, gepIndices,
- "localCall", afterOld);
- new StoreInst(ConstantInt::get(tBool, 1), oldGEP, true, afterOld);
- GetElementPtrInst* oldGGEP = GetElementPtrInst::Create(arrGlobal, gepIndices,
- "globalCall", afterOld);
- new StoreInst(ConstantInt::get(tBool, 1), oldGGEP, true, afterOld);
-
- // there will be no second call if the function body was not replicated
- if(newCall){
- Instruction* afterNew = nextInst(newCall);
- GetElementPtrInst* newGEP = GetElementPtrInst::Create(arrInst, gepIndices,
- "localCall", afterNew);
- new StoreInst(ConstantInt::get(tBool, 1), newGEP, true, afterNew);
- GetElementPtrInst* newGGEP = GetElementPtrInst::Create(arrGlobal,
- gepIndices, "globalCall", afterNew);
- new StoreInst(ConstantInt::get(tBool, 1), newGGEP, true, afterNew);
- }
-
- // write out the call's function, line number, and index
- writeOneCall(oldCall, curIdx);
-
- curIdx++;
- }
-
- return true;
-}
+ for (set::iterator i = fCalls.begin(), e = fCalls.end(); i != e; ++i)
+ {
+ CallInst &call = **i;
+ assert(!call.getCalledFunction() ||
+ !call.getCalledFunction()->isIntrinsic());
-// ***
-// doInitialization: Insert the necessary global variable before
-// instrumenting each function.
-// ***
-bool CallCoverage::doInitialization(Module& M){
- if(InfoFile == ""){
- cerr << "ERROR: CC cannot continue. -cc-info-file [file] is required.\n";
- exit(1);
- }
- _infoStream.open(InfoFile.c_str(), ios::out | ios::trunc);
- if(!_infoStream.is_open()){
- cerr << "ERROR: unable to open cc-file location: " << InfoFile << endl;
- exit(2);
- }
- else
- DEBUG(string("Output stream opened to ") + InfoFile.c_str());
-
- Context = &M.getContext();
- Builder = new DIBuilder(M);
-
- // find all call nodes in each function in the module (before replicating
- // the body) so it knows how big to make the global (and local) array for
- // each function
- functionCalls.clear();
- for(Module::iterator F = M.begin(), e = M.end(); F != e; ++F){
- if(F->isDeclaration() || F->isIntrinsic() ||
- F->getName().substr(0, 5).equals("__PT_"))
- continue;
+ // add instrumentation to set local and global coverage bits
+ IRBuilder<> builder(nextInst(&call));
+ insertArrayStoreInsts(arrays, curIdx, builder);
- //unsigned int funcCounter = 0;
- set fCalls;
- for(inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i){
- Instruction* inst = &(*i);
-
- if(CallInst* castInst = dyn_cast(inst)){
- // if we can't get the called function it very likely indicates
- // a function pointer, so we should still instrument
- if(castInst->getCalledFunction() &&
- castInst->getCalledFunction()->isIntrinsic()){
- // don't waste time for intrinsics.
- // NOTE: could also exclude other uninteresting functions here
- continue;
- }
-
- fCalls.insert(castInst);
- }
+ // write out the instrumentation site's static location details
+ writeOneCall(&call, curIdx++, true);
}
-
- //functionArraySize[F] = funcCounter;
- functionCalls[F] = fCalls;
- }
-
- funcToGlobalArray.clear();
-
- // the type of bool
- Type* tInt = Type::getInt8Ty(*Context);
-
- // some debug info preliminaries
- DIBuilder* globalBuilder = new DIBuilder(M);
- globalBuilder->createCompileUnit(dwarf::DW_LANG_C99, M.getModuleIdentifier(),
- "", getPassName(), false, "", 0);
- DIType boolType = globalBuilder->createBasicType("__cc_bool", 8, 8,
- dwarf::DW_ATE_boolean);
- // loop to make globals for each function
- for(Module::iterator F = M.begin(), e = M.end(); F != e; ++F){
- unsigned int arraySize = functionCalls[F].size();
- if(arraySize < 1)
- continue;
- Type* tArr = ArrayType::get(tInt, arraySize);
- GlobalVariable* theGlobal = new GlobalVariable(M, tArr, false,
- GlobalValue::ExternalLinkage,
- Constant::getNullValue(tArr),
- "__CC_arr_" + F->getName().str());
- funcToGlobalArray[F] = theGlobal;
-
- // create type debug info for new variables
- Value* subscript = globalBuilder->getOrCreateSubrange(0, arraySize-1);
- DIArray subscriptArray = globalBuilder->getOrCreateArray(subscript);
- DIType arrType = globalBuilder->createArrayType(arraySize*8, 8, boolType,
- subscriptArray);
- globalBuilder->createGlobalVariable(theGlobal->getName(), DIFile(),
- 0, arrType, false, theGlobal);
- }
-
- globalBuilder->finalize();
- return true;
+ // write out uninstrumented sites
+ unsigned int uninstIdx = 1;
+ for (ExtrinsicCalls::iterator call = calls.begin(); call != calls.end(); ++call)
+ if (!fCalls.count(call))
+ writeOneCall(call, uninstIdx++, false);
}
-// ***
-// doFinalization: Close the stream for the info file writing.
-// ***
-bool CallCoverage::doFinalization(Module& M){
- (void)M; // suppress warning
-
- if(_infoStream.is_open())
- _infoStream.close();
- return false;
+
+bool CallCoverage::runOnModule(Module &module){
+ static bool runBefore;
+ return runOnModuleOnce(module, options.infoFile, runBefore);
}
diff --git a/instrumentor/CallCoverage.h b/instrumentor/CallCoverage.h
index 37e13ad..ff329ba 100644
--- a/instrumentor/CallCoverage.h
+++ b/instrumentor/CallCoverage.h
@@ -5,7 +5,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,65 +22,40 @@
#ifndef CSI_CALL_COVERAGE_H
#define CSI_CALL_COVERAGE_H
-#include "PathTracing.h"
-
-#include
+#include "ExtrinsicCalls.h"
+#include "LocalCoveragePass.h"
#include "llvm_proxy/CFG.h"
#include "llvm_proxy/DebugInfo.h"
-#include "llvm_proxy/DIBuilder.h"
#include "llvm_proxy/Instructions.h"
-#include
-#include
-#include
-
namespace csi_inst {
// ---------------------------------------------------------------------------
-// CallCoverage is a function pass which does call coverage instrumentation
+// CallCoverage is a module pass which does call coverage instrumentation
// ---------------------------------------------------------------------------
-class CallCoverage : public llvm::FunctionPass {
+class CallCoverage : public LocalCoveragePass {
private:
- // Current context for multi threading support.
- llvm::LLVMContext* Context;
- // The Debug Info Builder for this module
- llvm::DIBuilder* Builder;
+ static Options options;
+
+ // Perform module-level tasks, open streams, and instrument each function
+ bool runOnModule(llvm::Module &M);
- // Insert necessary global variables for functions to use
- bool doInitialization(llvm::Module &M);
- // Finish up by closing the info file stream
- bool doFinalization(llvm::Module& M);
- // Instrument each function with coverage stuff on each call
- bool runOnFunction(llvm::Function &F);
- // Write out the function data for the info file
- void writeFunctionValue(llvm::Function& F);
// Write out the information for one call within a function
- void writeOneCall(llvm::CallInst* theCall, unsigned int index);
+ void writeOneCall(llvm::CallInst* theCall, unsigned int index,
+ bool isInstrumented=true);
- std::ofstream _infoStream; // The output stream to the info file (managed
- // by runOnFunction and written to as we go)
+ // Instrument each function for coverage on each call
+ void instrumentFunction(llvm::Function &);
public:
-
- // the mapping from functions to the list of instrumented
- // call-sites future passes (i.e. CallCoverage) will have for that function
- std::map > functionCalls;
- // the mapping from functions to their global array
- std::map funcToGlobalArray;
-
+ static const CoveragePassNames names;
static char ID; // Pass identification, replacement for typeid
- CallCoverage() : FunctionPass(ID) {}
+ CallCoverage() : LocalCoveragePass(ID, names) {}
virtual const char *getPassName() const {
return "Intra/Interprocedural Call Coverage Instrumentation";
}
-
- // We need the mapping information from the path tracing instrumentation
- //void getAnalysisUsage(AnalysisUsage &AU) const {
- // //AU.setPreservesCFG();
- // AU.addRequired();
- //}
};
} // end csi_inst namespace
diff --git a/instrumentor/CoveragePass.cpp b/instrumentor/CoveragePass.cpp
new file mode 100644
index 0000000..0816f20
--- /dev/null
+++ b/instrumentor/CoveragePass.cpp
@@ -0,0 +1,136 @@
+//===------------------------- CoveragePass.cpp ---------------------------===//
+//
+// Superclass for all program coverage passes. Shared functionality includes:
+// formatting of function-level metadata, metadata preliminaries for compilation
+// modules, and ensuring that instrumentation occurs only once for each
+// function.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#include "CoveragePass.h"
+#include "CoveragePassNames.h"
+#include "InfoFileOption.h"
+#include "PrepareCSI.h"
+#include "ScopedDIBuilder.h"
+
+#include "llvm_proxy/GlobalVariable.h"
+#include "llvm_proxy/Module.h"
+
+using namespace llvm;
+using namespace std;
+
+
+csi_inst::CoveragePass::CoveragePass(char &id, const CoveragePassNames &names)
+ : ModulePass(id),
+ names(names)
+{
+}
+
+
+void csi_inst::CoveragePass::writeFunctionValue(const Function &function, const GlobalVariable &global)
+{
+ infoStream << '#' << function.getName().str() << '|'
+ << global.getName().str() << '\n';
+}
+
+
+bool csi_inst::CoveragePass::prepareForModule(bool &runBefore, const Module &module, const InfoFileOption &infoFile)
+{
+ if (runBefore)
+ return false;
+ runBefore = true;
+
+ infoFile.open(infoStream);
+
+ // find out if the module contains any instrumentation-enabled functions
+ bool anyEnabledFuncs = false;
+ PrepareCSI& instData = getAnalysis();
+ for (Module::const_iterator i = module.begin(), e = module.end(); i != e; ++i)
+ if(instData.hasInstrumentationType(*i, names.upperShort))
+ {
+ anyEnabledFuncs = true;
+ break;
+ }
+
+ // if we don't do any instrumentation, no changes are necessary
+ if (!anyEnabledFuncs)
+ {
+ infoStream.close();
+ return false;
+ }
+
+ return true;
+}
+
+
+void csi_inst::CoveragePass::modulePreliminaries(Module &module, DIBuilder &debugBuilder)
+{
+ debugBuilder.createCompileUnit(dwarf::DW_LANG_C99,
+ module.getModuleIdentifier() + "$" + getPassName(),
+ "", getPassName(), false, "", 0);
+ boolType = debugBuilder.createBasicType("__" + names.lowerShort + "_bool", 8, 8,
+ dwarf::DW_ATE_boolean);
+
+ // the type of bool
+ LLVMContext &Context = module.getContext();
+ tBool = Type::getInt8Ty(Context);
+}
+
+
+// make globals and do instrumentation for each function
+void csi_inst::CoveragePass::instrumentFunctions(Module &module)
+{
+ const PrepareCSI& plan = getAnalysis();
+ for (Module::iterator function = module.begin(), end = module.end(); function != end; ++function)
+ if (!function->isDeclaration() && !function->isIntrinsic() &&
+ !function->getName().substr(0, 5).equals("__PT_") &&
+ plan.hasInstrumentationType(*function, names.upperShort))
+ instrumentFunction(*function);
+}
+
+
+void csi_inst::CoveragePass::getAnalysisUsage(AnalysisUsage &usage) const
+{
+ usage.setPreservesCFG();
+ requireAndPreserve(usage);
+}
+
+
+bool csi_inst::CoveragePass::runOnModuleOnce(Module &module, const InfoFileOption &infoFile, bool &runBefore)
+{
+ if (!prepareForModule(runBefore, module, infoFile))
+ return false;
+
+ // some debug info preliminaries
+ ScopedDIBuilder debugBuilder(module);
+ modulePreliminaries(module, debugBuilder);
+
+ // make globals and do instrumentation for each function
+ instrumentFunctions(module);
+
+ infoStream.close();
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+csi_inst::CoveragePass::Options::Options(const CoveragePassNames &names)
+ : infoFile(names)
+{
+}
diff --git a/instrumentor/CoveragePass.h b/instrumentor/CoveragePass.h
new file mode 100644
index 0000000..872412a
--- /dev/null
+++ b/instrumentor/CoveragePass.h
@@ -0,0 +1,95 @@
+//===-------------------------- CoveragePass.h ----------------------------===//
+//
+// Superclass for all program coverage passes. Shared functionality includes:
+// formatting of function-level metadata, metadata preliminaries for compilation
+// modules, and ensuring that instrumentation occurs only once for each
+// function.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_COVERAGE_PASS_H
+#define CSI_COVERAGE_PASS_H
+
+#include "InfoFileOption.h"
+
+#include
+
+#include "llvm_proxy/DebugInfo.h"
+
+#include
+#include
+
+namespace llvm
+{
+ class DIBuilder;
+ class GlobalVariable;
+}
+
+
+namespace csi_inst
+{
+ struct CoveragePassNames;
+
+
+ class CoveragePass : public llvm::ModulePass
+ {
+ private:
+ bool prepareForModule(bool &, const llvm::Module &, const InfoFileOption &);
+ void modulePreliminaries(llvm::Module &, llvm::DIBuilder &);
+ void instrumentFunctions(llvm::Module &);
+ virtual void instrumentFunction(llvm::Function &) = 0;
+
+ protected:
+ const CoveragePassNames &names;
+
+ struct Options
+ {
+ InfoFileOption infoFile;
+ Options(const CoveragePassNames &);
+ };
+
+ // info file (managed by runOnFunction and written to as we go)
+ std::ofstream infoStream;
+
+ // type used for storing coverage bits
+ llvm::Type *tBool;
+ llvm::DIType boolType;
+
+ CoveragePass(char &, const CoveragePassNames &);
+
+ template static void requireAndPreserve(llvm::AnalysisUsage &);
+
+ bool runOnModuleOnce(llvm::Module &, const InfoFileOption &, bool &);
+ void writeFunctionValue(const llvm::Function &, const llvm::GlobalVariable &);
+
+ public:
+ void getAnalysisUsage(llvm::AnalysisUsage &) const;
+ };
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+template void csi_inst::CoveragePass::requireAndPreserve(llvm::AnalysisUsage &usage)
+{
+ usage.addRequired();
+ usage.addPreserved();
+}
+
+
+#endif // !CSI_COVERAGE_PASS_H
diff --git a/instrumentor/CoveragePassNames.h b/instrumentor/CoveragePassNames.h
new file mode 100644
index 0000000..f12c7f5
--- /dev/null
+++ b/instrumentor/CoveragePassNames.h
@@ -0,0 +1,40 @@
+//===----------------------- CoveragePassNames.h --------------------------===//
+//
+// Uniform storage format for names of coverage passes (and different
+// shortenings, e.g. "Function Coverage" and "FC").
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_COVERAGE_PASS_NAMES_H
+#define CSI_COVERAGE_PASS_NAMES_H
+
+#include
+
+
+namespace csi_inst
+{
+ struct CoveragePassNames
+ {
+ const std::string lowerShort;
+ const std::string upperShort;
+ const std::string lowerFull;
+ const std::string titleFull;
+ };
+}
+
+
+#endif // !CSI_COVERAGE_PASS_NAMES_H
diff --git a/instrumentor/ExtrinsicCalls.cpp b/instrumentor/ExtrinsicCalls.cpp
new file mode 100644
index 0000000..cb68c3b
--- /dev/null
+++ b/instrumentor/ExtrinsicCalls.cpp
@@ -0,0 +1,35 @@
+//===------------------------ ExtrinsicCalls.cpp --------------------------===//
+//
+// Utilities for iterating over non-intrinsic call instructions.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#include "ExtrinsicCalls.h"
+
+using namespace llvm;
+
+
+csi_inst::ExtrinsicCalls csi_inst::extrinsicCalls(BasicBlock &block)
+{
+ return ExtrinsicCalls(block.begin(), block.end());
+}
+
+
+csi_inst::ExtrinsicCalls csi_inst::extrinsicCalls(Function &function)
+{
+ return ExtrinsicCalls(inst_begin(function), inst_end(function));
+}
diff --git a/instrumentor/ExtrinsicCalls.h b/instrumentor/ExtrinsicCalls.h
new file mode 100644
index 0000000..f3d0d6c
--- /dev/null
+++ b/instrumentor/ExtrinsicCalls.h
@@ -0,0 +1,170 @@
+//===------------------------- ExtrinsicCalls.h ---------------------------===//
+//
+// Utilities for iterating over non-intrinsic call instructions.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_EXTRINSIC_CALLS_H
+#define CSI_EXTRINSIC_CALLS_H
+
+#include "llvm_proxy/Function.h"
+#include "llvm_proxy/InstIterator.h"
+#include "llvm_proxy/Instructions.h"
+
+#include
+
+
+namespace csi_inst
+{
+ // utility for iterating over non-intrinsic call instructions
+ template
+ class ExtrinsicCalls
+ {
+ public:
+ ExtrinsicCalls(const InstructionIterator &begin,
+ const InstructionIterator &end);
+
+ class iterator
+ {
+ public:
+ iterator(const InstructionIterator ¤t, const InstructionIterator &end);
+
+ iterator &operator++();
+ operator llvm::CallInst *() const;
+ llvm::CallInst &operator*() const;
+ bool operator!=(const iterator &) const;
+
+ private:
+ InstructionIterator current;
+ const InstructionIterator end;
+
+ bool included() const;
+ void advance();
+ };
+
+ const iterator &begin() const;
+ const iterator &end() const;
+
+ private:
+ const iterator begin_;
+ const iterator end_;
+ };
+
+
+ ExtrinsicCalls extrinsicCalls(llvm::BasicBlock &);
+ ExtrinsicCalls extrinsicCalls(llvm::Function &);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+template
+csi_inst::ExtrinsicCalls::iterator::iterator(const InstructionIterator ¤t,
+ const InstructionIterator &end)
+ : current(current),
+ end(end)
+{
+ if (!included()) advance();
+}
+
+
+template inline
+typename csi_inst::ExtrinsicCalls::iterator &
+csi_inst::ExtrinsicCalls::iterator::operator++()
+{
+ advance();
+ return *this;
+}
+
+
+template inline
+csi_inst::ExtrinsicCalls::iterator::operator llvm::CallInst *() const
+{
+ assert(current != end);
+ llvm::Instruction * const instruction = &*current;
+ assert(llvm::isa(instruction));
+ return static_cast(instruction);
+}
+
+
+template inline
+llvm::CallInst &
+csi_inst::ExtrinsicCalls::iterator::operator*() const
+{
+ llvm::CallInst * const instruction = *this;
+ return *instruction;
+}
+
+
+template inline
+bool
+csi_inst::ExtrinsicCalls::iterator::operator!=(const iterator &other) const
+{
+ return current != other.current;
+}
+
+
+template
+bool csi_inst::ExtrinsicCalls::iterator::included() const
+{
+ if (current == end) return true;
+ const llvm::CallInst * const callInst = llvm::dyn_cast(&*current);
+ if (!callInst) return false;
+ const llvm::Function * const callee = callInst->getCalledFunction();
+ if (callee && callee->isIntrinsic()) return false;
+ return true;
+}
+
+
+template
+void csi_inst::ExtrinsicCalls::iterator::advance()
+{
+ if (current != end) ++current;
+ if (!included()) advance();
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+template
+csi_inst::ExtrinsicCalls::ExtrinsicCalls(const InstructionIterator &begin,
+ const InstructionIterator &end)
+ : begin_(begin, end),
+ end_(end, end)
+{
+}
+
+
+template inline
+const typename csi_inst::ExtrinsicCalls::iterator &
+csi_inst::ExtrinsicCalls::begin() const
+{
+ return begin_;
+}
+
+
+template inline
+const typename csi_inst::ExtrinsicCalls::iterator &
+csi_inst::ExtrinsicCalls::end() const
+{
+ return end_;
+}
+
+
+#endif // !CSI_EXTRINSIC_CALLS_H
diff --git a/instrumentor/FuncCoverage.cpp b/instrumentor/FuncCoverage.cpp
new file mode 100644
index 0000000..bb4c181
--- /dev/null
+++ b/instrumentor/FuncCoverage.cpp
@@ -0,0 +1,80 @@
+//===------------------------- FuncCoverage.cpp ---------------------------===//
+//
+// This pass instruments function entry points for interprocedural
+// analysis by gathering global function coverage information.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "func-coverage"
+
+#include "CoveragePassNames.h"
+#include "FuncCoverage.h"
+#include "PrepareCSI.h"
+#include "ScopedDIBuilder.h"
+#include "Utils.hpp"
+
+#include
+#include
+
+#include "llvm_proxy/InstIterator.h"
+#include "llvm_proxy/IntrinsicInst.h"
+#include "llvm_proxy/Module.h"
+
+#include
+
+using namespace csi_inst;
+using namespace llvm;
+using namespace std;
+
+
+const CoveragePassNames FuncCoverage::names = {
+ "fc",
+ "FC",
+ "function",
+ "Function",
+};
+
+FuncCoverage::Options FuncCoverage::options(names);
+
+// Register function coverage as a pass
+char FuncCoverage::ID = 0;
+static RegisterPass X("fn-coverage",
+ "Insert function coverage instrumentation",
+ false, false);
+
+
+void FuncCoverage::instrumentFunction(Function &function)
+{
+ // create new global variable to hold this function's coverage bit
+ DIBuilder debugBuilder(*function.getParent());
+ GlobalVariable &theGlobal = getOrCreateGlobal(debugBuilder, function, *tBool, boolType, names.upperShort);
+
+ // write out the function name and its arrays
+ writeFunctionValue(function, theGlobal);
+
+ // instrument the function's entry
+ Instruction* insertPoint = function.getEntryBlock().getFirstInsertionPt();
+ new StoreInst(ConstantInt::get(tBool, 1), &theGlobal, false, 1,
+ Unordered, CrossThread, insertPoint);
+}
+
+
+bool FuncCoverage::runOnModule(Module& module)
+{
+ static bool runBefore;
+ return runOnModuleOnce(module, options.infoFile, runBefore);
+}
diff --git a/instrumentor/FuncCoverage.h b/instrumentor/FuncCoverage.h
new file mode 100644
index 0000000..f7f88d1
--- /dev/null
+++ b/instrumentor/FuncCoverage.h
@@ -0,0 +1,63 @@
+//===-------------------------- FuncCoverage.h ----------------------------===//
+//
+// This pass instruments function entry points for interprocedural
+// analysis by gathering global function coverage information.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_FN_COVERAGE_H
+#define CSI_FN_COVERAGE_H
+
+#include "CoveragePass.h"
+
+#include
+
+#include "llvm_proxy/CFG.h"
+#include "llvm_proxy/DebugInfo.h"
+#include "llvm_proxy/Instructions.h"
+
+#include
+#include
+#include
+
+namespace csi_inst {
+
+// ---------------------------------------------------------------------------
+// FuncCoverage is a module pass which does function coverage instrumentation
+// ---------------------------------------------------------------------------
+class FuncCoverage : public CoveragePass {
+private:
+ static Options options;
+
+ // Perform module-level tasks, open streams, and instrument each function
+ bool runOnModule(llvm::Module &M);
+
+ // Instrument each function with coverage at entry
+ void instrumentFunction(llvm::Function &);
+
+public:
+ static const CoveragePassNames names;
+ static char ID; // Pass identification, replacement for typeid
+ FuncCoverage() : CoveragePass(ID, names) {}
+
+ virtual const char *getPassName() const {
+ return "Interprocedural Function Coverage Instrumentation";
+ }
+};
+} // end csi_inst namespace
+
+#endif
diff --git a/instrumentor/InfoFileOption.cpp b/instrumentor/InfoFileOption.cpp
new file mode 100644
index 0000000..47a8586
--- /dev/null
+++ b/instrumentor/InfoFileOption.cpp
@@ -0,0 +1,57 @@
+//===------------------------ InfoFileOption.cpp --------------------------===//
+//
+// A wrapper class to indicate output metadata files for instrumentation
+// passes, and manage their output streams.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "info-file-option"
+
+#include "CoveragePassNames.h"
+#include "InfoFileOption.h"
+#include "Utils.hpp"
+
+#include
+
+#include
+
+using namespace llvm;
+using namespace std;
+
+
+csi_inst::InfoFileOption::InfoFileOption(const CoveragePassNames &names)
+ : lowerShortName(names.lowerShort),
+ flag(names.lowerShort + "-info-file"),
+ description("The path to the " + names.lowerFull + " coverage info file."),
+ option(flag.c_str(),
+ cl::desc(description.c_str()),
+ cl::value_desc("file_path"))
+{
+}
+
+
+void csi_inst::InfoFileOption::open(ofstream &stream) const
+{
+ const string &value = option.getValue();
+ if (value.empty())
+ report_fatal_error(lowerShortName + " cannot continue: " + option.ArgStr + " [" + option.ValueStr + "] is required", false);
+
+ stream.open(value.c_str(), ios::out | ios::trunc);
+ if (!stream)
+ report_fatal_error("unable to open " + lowerShortName + "-file location: " + value, false);
+ DEBUG(dbgs() << "Output stream opened to " << value << "\n");
+}
diff --git a/instrumentor/InfoFileOption.h b/instrumentor/InfoFileOption.h
new file mode 100644
index 0000000..87f71bd
--- /dev/null
+++ b/instrumentor/InfoFileOption.h
@@ -0,0 +1,51 @@
+//===------------------------- InfoFileOption.h ---------------------------===//
+//
+// A wrapper class to indicate output metadata files for instrumentation
+// passes, and manage their output streams.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_INFO_FILE_H
+#define CSI_INFO_FILE_H
+
+#include
+
+#include
+#include
+
+
+namespace csi_inst
+{
+ struct CoveragePassNames;
+
+
+ class InfoFileOption
+ {
+ public:
+ InfoFileOption(const CoveragePassNames &);
+ void open(std::ofstream &) const;
+
+ private:
+ const std::string lowerShortName;
+ const std::string flag;
+ const std::string description;
+ llvm::cl::opt option;
+ };
+}
+
+
+#endif // !CSI_INFO_FILE_H
diff --git a/instrumentor/InstrumentationData.cpp b/instrumentor/InstrumentationData.cpp
new file mode 100644
index 0000000..d91257a
--- /dev/null
+++ b/instrumentor/InstrumentationData.cpp
@@ -0,0 +1,60 @@
+//===---------------------- InstrumentationData.cpp -----------------------===//
+//
+// This module contains utilities for CSI instrumentation preparation.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#include "BBCoverage.h"
+#include "CallCoverage.h"
+#include "CoveragePassNames.h"
+#include "FuncCoverage.h"
+#include "InstrumentationData.h"
+
+using namespace csi_inst;
+using namespace llvm;
+using namespace std;
+
+static bool hasCallsFilter(const set& scheme, Function* F){
+ if(!scheme.count("CC"))
+ return(true);
+
+ const ExtrinsicCalls calls = extrinsicCalls(*F);
+ return(calls.begin() != calls.end());
+}
+
+static bool coverageFilter(const set& scheme, Function*){
+ bool hasCC = scheme.count(CallCoverage::names.upperShort);
+ bool hasFC = scheme.count(FuncCoverage::names.upperShort);
+ bool hasBBC = scheme.count(BBCoverage::names.upperShort);
+ if(hasBBC && (hasFC || hasCC)) return(false);
+ return(true);
+}
+
+static const FilterFn csi_filters[] = {coverageFilter, hasCallsFilter};
+const vector csi_inst::Filters(csi_filters,
+ csi_filters + sizeof(csi_filters) /
+ sizeof(csi_filters[0]));
+
+static const string csi_inst_arr[] = {
+ BBCoverage::names.upperShort,
+ CallCoverage::names.upperShort,
+ FuncCoverage::names.upperShort,
+ "PT"
+};
+const set csi_inst::Instrumentors(csi_inst_arr,
+ csi_inst_arr + sizeof(csi_inst_arr) /
+ sizeof(csi_inst_arr[0]));
diff --git a/instrumentor/InstrumentationData.h b/instrumentor/InstrumentationData.h
new file mode 100644
index 0000000..f639f84
--- /dev/null
+++ b/instrumentor/InstrumentationData.h
@@ -0,0 +1,43 @@
+//===---------------------- InstrumentationData.h -------------------------===//
+//
+// This module contains utilities for CSI instrumentation preparation.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_INSTRUMENTATION_DATA_H
+#define CSI_INSTRUMENTATION_DATA_H
+
+#include "llvm_proxy/CFG.h"
+
+#include
+#include
+#include
+
+namespace csi_inst {
+
+// A vector of filters to apply over proposed schemes and functions they are
+// to be applied to. Filters do NOT modify the scheme. If the scheme is dopey,
+// it is simply rejected by returning false.
+// @return true if the scheme passes the filter; false otherwise.
+typedef bool(*FilterFn)(const std::set& scheme, llvm::Function* F);
+extern const std::vector Filters;
+
+extern const std::set Instrumentors;
+
+} // end csi_inst namespace
+
+#endif
diff --git a/instrumentor/LocalCoveragePass.cpp b/instrumentor/LocalCoveragePass.cpp
new file mode 100644
index 0000000..23c03d1
--- /dev/null
+++ b/instrumentor/LocalCoveragePass.cpp
@@ -0,0 +1,96 @@
+//===---------------------- LocalCoveragePass.cpp -------------------------===//
+//
+// Superclass for all program coverage passes that gather stack-local coverage
+// data. Shared functionality includes: declaration of local arrays, and
+// storage of boolean data.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#include "CoveragePassNames.h"
+#include "LocalCoveragePass.h"
+#include "Utils.hpp"
+
+#include "llvm_proxy/IRBuilder.h"
+#include "llvm_proxy/Module.h"
+
+using namespace llvm;
+using namespace std;
+
+
+csi_inst::LocalCoveragePass::LocalCoveragePass(char &id, const CoveragePassNames &names)
+ : CoveragePass(id, names),
+ lowerShortName(names.lowerShort)
+{
+}
+
+
+void csi_inst::LocalCoveragePass::getAnalysisUsage(AnalysisUsage &usage) const
+{
+ CoveragePass::getAnalysisUsage(usage);
+}
+
+
+csi_inst::LocalCoveragePass::CoverageArrays csi_inst::LocalCoveragePass::prepareFunction(Function &function, unsigned arraySize, const SilentInternalOption &silentInternal)
+{
+ ArrayType * const tArr = ArrayType::get(tBool, arraySize);
+
+ // create global coverage array
+ DIBuilder debugBuilder(*function.getParent());
+ const DIType arrType = createArrayType(debugBuilder, arraySize, boolType);
+ GlobalVariable &theGlobal = getOrCreateGlobal(debugBuilder, function, *tArr, arrType, names.upperShort);
+
+ // declare the local coverage array and set up debug metadata
+ AllocaInst * const arrInst = createZeroedLocalArray(function, *tArr, "__" + names.upperShort + "_arr", debugBuilder, boolType, silentInternal);
+
+ // write out the function name and its arrays
+ writeFunctionValue(function, theGlobal);
+
+ const CoverageArrays result = { theGlobal, *arrInst };
+ return result;
+}
+
+
+void csi_inst::LocalCoveragePass::insertArrayStoreInsts(const CoverageArrays &arrays, unsigned index, IRBuilder<> &builder) const
+{
+ Type * const intType = builder.getInt32Ty();
+
+ Value * const trueValue = ConstantInt::get(tBool, true);
+ Value * const gepIndices[] = {
+ Constant::getNullValue(intType),
+ ConstantInt::get(intType, index),
+ };
+
+ builder.CreateStore(trueValue, builder.CreateInBoundsGEP(&arrays.local, gepIndices, "local" + names.upperShort), true);
+#if LLVM_VERSION < 30200
+ StoreInst * const globalStore = builder.CreateStore(trueValue, builder.CreateInBoundsGEP(&arrays.global, gepIndices, "global" + names.upperShort), false);
+ globalStore->setAlignment(1);
+#else
+ StoreInst * const globalStore = builder.CreateAlignedStore(trueValue, builder.CreateInBoundsGEP(&arrays.global, gepIndices, "global" + names.upperShort), 1, false);
+#endif
+ globalStore->setOrdering(Unordered);
+ globalStore->setSynchScope(CrossThread);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+csi_inst::LocalCoveragePass::Options::Options(const CoveragePassNames &names)
+ : CoveragePass::Options(names),
+ silentInternal(names)
+{
+}
diff --git a/instrumentor/LocalCoveragePass.h b/instrumentor/LocalCoveragePass.h
new file mode 100644
index 0000000..8c52951
--- /dev/null
+++ b/instrumentor/LocalCoveragePass.h
@@ -0,0 +1,70 @@
+//===----------------------- LocalCoveragePass.h --------------------------===//
+//
+// Superclass for all program coverage passes that gather stack-local coverage
+// data. Shared functionality includes: declaration of local arrays, and
+// storage of boolean data.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_LOCAL_COVERAGE_PASS_H
+#define CSI_LOCAL_COVERAGE_PASS_H
+
+#include "CoveragePass.h"
+#include "SilentInternalOption.h"
+
+#include "llvm_proxy/BasicBlock.h"
+#include "llvm_proxy/IRBuilder.h"
+
+namespace llvm
+{
+ class AllocaInst;
+ class Instruction;
+}
+
+
+namespace csi_inst
+{
+ class LocalCoveragePass : public CoveragePass
+ {
+ private:
+ const std::string &lowerShortName;
+
+ protected:
+ struct Options : public CoveragePass::Options
+ {
+ SilentInternalOption silentInternal;
+ Options(const CoveragePassNames &);
+ };
+
+ LocalCoveragePass(char &, const CoveragePassNames &);
+
+ struct CoverageArrays
+ {
+ llvm::GlobalVariable &global;
+ llvm::AllocaInst &local;
+ };
+
+ CoverageArrays prepareFunction(llvm::Function &, unsigned, const SilentInternalOption &);
+ void insertArrayStoreInsts(const CoverageArrays &, unsigned, llvm::IRBuilder<> &) const;
+
+ public:
+ void getAnalysisUsage(llvm::AnalysisUsage &) const;
+ };
+}
+
+
+#endif // !CSI_LOCAL_COVERAGE_PASS_H
diff --git a/instrumentor/PathNumbering.cpp b/instrumentor/PathNumbering.cpp
index f8f1430..2d425e0 100644
--- a/instrumentor/PathNumbering.cpp
+++ b/instrumentor/PathNumbering.cpp
@@ -13,7 +13,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -140,11 +140,11 @@ std::string PPBallLarusNode::getName() {
if(getBlock() != NULL) {
if(getBlock()->hasName()) {
std::string tempName(getBlock()->getName());
- name << tempName.c_str() << " (" << _uid << ")";
+ name << tempName.c_str() << " (" << _uid << ')';
} else
- name << " (" << _uid << ")";
+ name << " (" << _uid << ')';
} else
- name << " (" << _uid << ")";
+ name << " (" << _uid << ')';
return name.str();
}
@@ -268,10 +268,10 @@ void PPBallLarusDag::calculatePathNumbers() {
std::queue bfsQueue;
bfsQueue.push(getExit());
- while(bfsQueue.size() > 0) {
+ while(!bfsQueue.empty()) {
node = bfsQueue.front();
- DEBUG(dbgs() << "calculatePathNumbers on " << node->getName() << "\n");
+ DEBUG(dbgs() << "calculatePathNumbers on " << node->getName() << '\n');
bfsQueue.pop();
unsigned long prevPathNumber = node->getNumberPaths();
@@ -281,7 +281,7 @@ void PPBallLarusDag::calculatePathNumbers() {
//if( node->getNumberPaths() > 100000000 && node != getRoot() ) {
if (node->getNumberPaths() > (ULONG_MAX / 4) && node != getRoot()){
DEBUG(dbgs() << "WARNING: DAG splitting occurred for function "
- << getFunction().getName().str() << "\n");
+ << getFunction().getName().str() << '\n');
// Add new phony edge from the split-node to the DAG's exit
PPBallLarusEdge* exitEdge = addEdge(node, getExit(), 0);
@@ -321,7 +321,7 @@ void PPBallLarusDag::calculatePathNumbers() {
<< node->getNumberPaths() << ".\n");
if(prevPathNumber == 0 && node->getNumberPaths() != 0) {
- DEBUG(dbgs() << "node ready : " << node->getName() << "\n");
+ DEBUG(dbgs() << "node ready : " << node->getName() << '\n');
for(PPBLEdgeIterator pred = node->predBegin(), end = node->predEnd();
pred != end; pred++) {
if( (*pred)->getType() == PPBallLarusEdge::BACKEDGE ||
@@ -336,7 +336,7 @@ void PPBallLarusDag::calculatePathNumbers() {
}
}
- DEBUG(dbgs() << "\tNumber of paths: " << getRoot()->getNumberPaths() << "\n");
+ DEBUG(dbgs() << "\tNumber of paths: " << getRoot()->getNumberPaths() << '\n');
}
// Returns the number of paths for the Dag.
diff --git a/instrumentor/PathNumbering.h b/instrumentor/PathNumbering.h
index 0a4f30f..38c1f71 100644
--- a/instrumentor/PathNumbering.h
+++ b/instrumentor/PathNumbering.h
@@ -13,7 +13,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/instrumentor/PathTracing.cpp b/instrumentor/PathTracing.cpp
index 5a4782d..44ba23f 100644
--- a/instrumentor/PathTracing.cpp
+++ b/instrumentor/PathTracing.cpp
@@ -9,7 +9,7 @@
// http://portal.acm.org/citation.cfm?id=243857
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -33,26 +33,28 @@
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "path-tracing"
+#include "BBCoverage.h"
#include "PathTracing.h"
-
-#include "Versions.h"
+#include "PrepareCSI.h"
+#include "Utils.hpp"
#include
#include
-#include
#include
#include
+#include
#include
+#include "Versions.h"
+#include "llvm_proxy/DIBuilder.h"
#include "llvm_proxy/Module.h"
+#include "llvm_proxy/InstIterator.h"
#include "llvm_proxy/IntrinsicInst.h"
#include
+#include
#include
#include
-#include
-
-#include
using namespace csi_inst;
using namespace llvm;
@@ -98,7 +100,7 @@ static cl::opt ArraySize("pt-path-array-size", cl::desc("Set the size "
"functions. Default: 10"),
cl::value_desc("path_array_size"));
-static cl::opt TrackerFile("pt-tracker-file", cl::desc("The path to "
+static cl::opt TrackerFile("pt-info-file", cl::desc("The path to "
"the increment-line-number output file."),
cl::value_desc("file_path"));
@@ -326,7 +328,7 @@ void BLInstrumentationDag::splitUpdate(BLInstrumentationEdge* formerEdge,
// implementation does not try to minimize the instrumentation overhead
// by trying to find hot edges.
void BLInstrumentationDag::calculateSpanningTree() {
- std::stack dfsStack;
+ stack dfsStack;
for(PPBLNodeIterator nodeIt = _nodes.begin(), end = _nodes.end();
nodeIt != end; nodeIt++) {
@@ -334,7 +336,7 @@ void BLInstrumentationDag::calculateSpanningTree() {
}
dfsStack.push(getRoot());
- while(dfsStack.size() > 0) {
+ while(!dfsStack.empty()) {
PPBallLarusNode* node = dfsStack.top();
dfsStack.pop();
@@ -443,7 +445,7 @@ Value* BLInstrumentationNode::getStartingPathNumber(){
void BLInstrumentationNode::setStartingPathNumber(Value* pathNumber) {
DEBUG(dbgs() << " SPN-" << getName() << " <-- " << (pathNumber ?
pathNumber->getName() :
- "unused") << "\n");
+ "unused") << '\n');
_startingPathNumber = pathNumber;
}
@@ -453,7 +455,7 @@ Value* BLInstrumentationNode::getEndingPathNumber(){
void BLInstrumentationNode::setEndingPathNumber(Value* pathNumber) {
DEBUG(dbgs() << " EPN-" << getName() << " <-- "
- << (pathNumber ? pathNumber->getName() : "unused") << "\n");
+ << (pathNumber ? pathNumber->getName() : "unused") << '\n');
_endingPathNumber = pathNumber;
}
@@ -624,13 +626,14 @@ void PathTracing::insertCounterIncrement(Value* incValue,
// Counter increment for array
if( dag->getNumberOfPaths() <= HASH_THRESHHOLD ) {
// Get pointer to the array location
- std::vector gepIndices(2);
- gepIndices[0] = Constant::getNullValue(Type::getInt64Ty(*Context));
- gepIndices[1] = curLoc;
+ Value * const gepIndices[] = {
+ Constant::getNullValue(Type::getInt64Ty(*Context)),
+ curLoc,
+ };
GetElementPtrInst* pcPointer =
- GetElementPtrInst::Create(dag->getCounterArray(), gepIndices,
- "arrLoc", insertPoint);
+ GetElementPtrInst::CreateInBounds(dag->getCounterArray(), gepIndices,
+ "arrLoc", insertPoint);
// Store back in to the array
new StoreInst(incValue, pcPointer, true, insertPoint);
@@ -830,8 +833,9 @@ bool PathTracing::splitCritical(BLInstrumentationEdge* edge,
// NOTE: could handle inlining specially here if desired.
-void writeBBLineNums(BasicBlock* bb, BLInstrumentationDag* dag,
- ostream& stream = cout){
+static void writeBBLineNums(BasicBlock* bb,
+ BLInstrumentationDag* dag,
+ raw_ostream& stream = outs()){
if(!bb){
stream << "|NULL";
return;
@@ -846,7 +850,7 @@ void writeBBLineNums(BasicBlock* bb, BLInstrumentationDag* dag,
dbLoc = i->getDebugLoc();
if(!dbLoc.isUnknown()){
- stream << "|" << dbLoc.getLine(); // << ":" << dbLoc.getCol();
+ stream << '|' << dbLoc.getLine(); // << ':' << dbLoc.getCol();
any = true;
}
else if(LoadInst* inst = dyn_cast(&*i)){
@@ -863,6 +867,13 @@ void writeBBLineNums(BasicBlock* bb, BLInstrumentationDag* dag,
stream << "|NULL";
}
+
+static void writeBBLineNums(BasicBlock* bb, BLInstrumentationDag* dag, ostream& stream){
+ raw_os_ostream wrapped(stream);
+ writeBBLineNums(bb, dag, wrapped);
+}
+
+
void PathTracing::writeBBs(Function& F, BLInstrumentationDag* dag){
BLInstrumentationEdge* root = (BLInstrumentationEdge*)dag->getExitRootEdge();
list edgeWl;
@@ -871,7 +882,7 @@ void PathTracing::writeBBs(Function& F, BLInstrumentationDag* dag){
// special handling here for exit node (mark as "EXIT")
BLInstrumentationNode* exitNode = (BLInstrumentationNode*)root->getSource();
- _trackerStream << exitNode->getNodeId() << "|EXIT" << endl;
+ trackerStream << exitNode->getNodeId() << "|EXIT" << '\n';
if(exitNode->getBlock()){
errs() << "ERROR: Exit node has associated basic block in function "
<< F.getName() << ". This is a tool error.\n";
@@ -881,9 +892,9 @@ void PathTracing::writeBBs(Function& F, BLInstrumentationDag* dag){
// special handling here for entry node (mark as "ENTRY" but also write line#)
BLInstrumentationNode* entryNode = (BLInstrumentationNode*)root->getTarget();
- _trackerStream << entryNode->getNodeId() << "|ENTRY";
- writeBBLineNums(entryNode->getBlock(), dag, _trackerStream);
- _trackerStream << endl;
+ trackerStream << entryNode->getNodeId() << "|ENTRY";
+ writeBBLineNums(entryNode->getBlock(), dag, trackerStream);
+ trackerStream << '\n';
for(PPBLEdgeIterator i = entryNode->succBegin(), e = entryNode->succEnd(); i != e; ++i){
edgeWl.push_back((BLInstrumentationEdge*)(*i));
}
@@ -896,9 +907,9 @@ void PathTracing::writeBBs(Function& F, BLInstrumentationDag* dag){
if(done.count(current))
continue;
- _trackerStream << current->getNodeId();
- writeBBLineNums(current->getBlock(), dag, _trackerStream);
- _trackerStream << endl;
+ trackerStream << current->getNodeId();
+ writeBBLineNums(current->getBlock(), dag, trackerStream);
+ trackerStream << '\n';
for(PPBLEdgeIterator i = current->succBegin(), e = current->succEnd(); i != e; ++i){
edgeWl.push_back((BLInstrumentationEdge*)(*i));
@@ -909,10 +920,10 @@ void PathTracing::writeBBs(Function& F, BLInstrumentationDag* dag){
}
void PathTracing::writeTrackerInfo(Function& F, BLInstrumentationDag* dag){
- _trackerStream << "#" << endl << F.getName().str() << endl;
+ trackerStream << "#\n" << F.getName().str() << '\n';
writeBBs(F, dag);
- _trackerStream << "$" << endl;
+ trackerStream << "$\n";
BLInstrumentationEdge* root = (BLInstrumentationEdge*)dag->getExitRootEdge();
list edgeWl;
@@ -938,15 +949,15 @@ void PathTracing::writeTrackerInfo(Function& F, BLInstrumentationDag* dag){
current->getType() == PPBallLarusEdge::SPLITEDGE){
BLInstrumentationEdge* phony =
(BLInstrumentationEdge*)current->getPhonyRoot();
- _trackerStream << source->getNodeId() << "~>" << target->getNodeId()
- << "|" << phony->getIncrement()
- << "$" << phony->getWeight() << endl;
+ trackerStream << source->getNodeId() << "~>" << target->getNodeId()
+ << '|' << phony->getIncrement()
+ << '$' << phony->getWeight() << '\n';
}
else{
- _trackerStream << source->getNodeId() << "->" << target->getNodeId()
- << "|" << current->getIncrement()
- << "$" << current->getWeight()
- << endl;
+ trackerStream << source->getNodeId() << "->" << target->getNodeId()
+ << '|' << current->getIncrement()
+ << '$' << current->getWeight()
+ << '\n';
}
for(PPBLEdgeIterator i = target->succBegin(), e = target->succEnd(); i != e; ++i){
@@ -962,22 +973,10 @@ void PathTracing::writeTrackerInfo(Function& F, BLInstrumentationDag* dag){
// Entry point of the function
bool PathTracing::runOnFunction(Function &F) {
- if (F.isDeclaration())
+ PrepareCSI& instData = getAnalysis();
+ if(F.isDeclaration() || !instData.hasInstrumentationType(F, "PT"))
return false;
- DEBUG(dbgs() << "Function: " << F.getName() << "\n");
-
- // clone the function to keep non-instrumented version
- ValueToValueMapTy valueMap;
- Function* newF = CloneFunction(&F, valueMap, false, NULL);
- newF->setName("__PT_" + F.getName());
-
- ValueToValueMapTy argsValueMap;
- for(Function::arg_iterator i = F.getArgumentList().begin(), e = F.getArgumentList().end(); i != e; ++i){
- argsValueMap[valueMap.lookup(&*i)] = i;
- }
-
- // in case we don't instrument, clear the clone-mapping right away
- this->oldToNewCallMap.clear();
+ DEBUG(dbgs() << "Function: " << F.getName() << '\n');
// Build DAG from CFG
BLInstrumentationDag dag = BLInstrumentationDag(F);
@@ -1021,13 +1020,13 @@ bool PathTracing::runOnFunction(Function &F) {
new StoreInst(ConstantInt::get(tInt, 0), trackInst, true, entryInst);
// Store the setinal value (-1) into pathArr[end]
- std::vector gepIndices(2);
- gepIndices[0] = Constant::getNullValue(Type::getInt64Ty(*Context));
- gepIndices[1] = ConstantInt::get(tInt, PATHS_SIZE-1);
- GetElementPtrInst* arrLast = GetElementPtrInst::Create(arrInst, gepIndices,
- "arrLast", entryInst);
- StoreInst* setinalEnd = new StoreInst(ConstantInt::get(tInt, -1), arrLast,
- true, entryInst);
+ Value * const gepIndices[] = {
+ Constant::getNullValue(Type::getInt64Ty(*Context)),
+ ConstantInt::get(tInt, PATHS_SIZE-1),
+ };
+ GetElementPtrInst* arrLast = GetElementPtrInst::CreateInBounds(arrInst, gepIndices,
+ "arrLast", entryInst);
+ new StoreInst(ConstantInt::get(tInt, -1), arrLast, true, entryInst);
dag.setCounterArray(arrInst);
dag.setCurIndex(idxInst);
@@ -1035,191 +1034,65 @@ bool PathTracing::runOnFunction(Function &F) {
this->setPathTracker(trackInst);
// create debug info for new variables
- DIType intType = Builder->createBasicType("__pt_int", 64, 64, 5);
- Value* subscript = Builder->getOrCreateSubrange(0, PATHS_SIZE-1);
- DIArray subscriptArray = Builder->getOrCreateArray(subscript);
- DIType arrType = Builder->createArrayType(PATHS_SIZE*64, 64, intType,
- subscriptArray);
+ DIBuilder Builder(*F.getParent());
+ DIType intType = Builder.createBasicType("__pt_int", 64, 64, dwarf::DW_ATE_signed);
+ const DIType arrType = createArrayType(Builder, PATHS_SIZE, intType);
// get the debug location of any instruction in this basic block--this will
// use the same info. If there is none, technically we should build it, but
// that's a huge pain (if it's possible) so I just give up right now
- BasicBlock* entryBB = entryInst->getParent();
- DebugLoc dbLoc;
- for(BasicBlock::iterator i = entryBB->begin(), e = entryBB->end(); i != e; ++i){
- dbLoc = i->getDebugLoc();
- if(!dbLoc.isUnknown())
- break;
- }
- if(dbLoc.isUnknown()){
- // try again iterating through the entire function...
- for(inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i){
- dbLoc = i->getDebugLoc();
- if(!dbLoc.isUnknown())
- break;
- }
-
- if(dbLoc.isUnknown() && !SilentInternal){
- errs() << "WARNING: there will be no debug locations for instrumented "
- << "function "+F.getName()+" . Traced paths will likely "
- << "be unextractable!\n";
- }
- else if(!SilentInternal){
- DEBUG(dbgs() << "WARNING: debug location outside of entry block used "
- << "for instrumented function "+F.getName()+"\n");
- }
- }
- else{
- DIVariable arrDI = Builder->createLocalVariable(
+ const DebugLoc dbLoc = findEarlyDebugLoc(F, SilentInternal);
+ if (!dbLoc.isUnknown()) {
+ DIVariable arrDI = Builder.createLocalVariable(
(unsigned)dwarf::DW_TAG_auto_variable,
DIDescriptor(dbLoc.getScope(*Context)),
"__PT_counter_arr",
DIFile(dbLoc.getScope(*Context)), 0, arrType, true);
- Instruction* declareArr = Builder->insertDeclare(arrInst, arrDI, entryInst);
- declareArr->setDebugLoc(dbLoc);
- DIVariable idxDI = Builder->createLocalVariable(
+ insertDeclare(Builder, arrInst, arrDI, dbLoc, entryInst);
+ DIVariable idxDI = Builder.createLocalVariable(
(unsigned)dwarf::DW_TAG_auto_variable,
DIDescriptor(dbLoc.getScope(*Context)),
"__PT_counter_idx",
DIFile(dbLoc.getScope(*Context)), 0, intType, true);
- Instruction* declareIdx = Builder->insertDeclare(idxInst, idxDI, entryInst);
- declareIdx->setDebugLoc(dbLoc);
- DIVariable trackDI = Builder->createLocalVariable(
+ insertDeclare(Builder, idxInst, idxDI, dbLoc, entryInst);
+ DIVariable trackDI = Builder.createLocalVariable(
(unsigned)dwarf::DW_TAG_auto_variable,
DIDescriptor(dbLoc.getScope(*Context)),
"__PT_current_path",
DIFile(dbLoc.getScope(*Context)), 0, intType,
true);
- Instruction* declareTrack = Builder->insertDeclare(trackInst, trackDI, entryInst);
- declareTrack->setDebugLoc(dbLoc);
+ insertDeclare(Builder, trackInst, trackDI, dbLoc, entryInst);
}
// do the instrumentation and write out the path info to the .info file
insertInstrumentation(dag);
writeTrackerInfo(F, &dag);
-
- // clone the original body back in
- SmallVector returns;
- CloneFunctionInto(&F, newF, argsValueMap, false, returns, "", NULL, NULL);
-
- // and set up the trampoline
- Type* tBool = Type::getInt8Ty(*Context);
- BasicBlock* oldEntry =
- cast(argsValueMap[&(newF->getEntryBlock())]);
- BasicBlock* curEntry = &(F.getEntryBlock());
- BasicBlock* newEntry = BasicBlock::Create(*Context, "newEntry", &F,
- &F.getEntryBlock());
- LoadInst* flag = new LoadInst(funcInstMap[&F], "instFlag", newEntry);
- ICmpInst* isInst = new ICmpInst(*newEntry, CmpInst::ICMP_NE, flag,
- ConstantInt::get(tBool, 0), "isInst");
- BranchInst::Create(curEntry, oldEntry, isInst, newEntry);
-
- // find last alloca instruction, include everything prior so the debug
- // info for local vars works
- list toMove;
- list possibleMove;
- for(BasicBlock::iterator i = curEntry->begin(), e = curEntry->end(); i != e; ++i){
- if(i != (void*)setinalEnd)
- possibleMove.push_back(&*i);
-
- if(dyn_cast(i)){
- toMove.splice(toMove.end(), possibleMove);
- possibleMove.clear();
- }
- }
- for(list::iterator i = toMove.begin(), e = toMove.end(); i != e; ++i){
- (*i)->moveBefore(flag);
- // remove the old version in old entry
- Value* origInst = argsValueMap[valueMap[*i]];
- if(origInst){
- Instruction* oldInst = dyn_cast(origInst);
- oldInst->replaceAllUsesWith(*i);
- oldInst->eraseFromParent();
- }
- }
-
- // then delete all remaining duplicate dbg.declare instrinsics
- set toRemove;
- for (BasicBlock::iterator i = oldEntry->begin(), e = oldEntry->end(); i != e; ++i){
- if(DbgDeclareInst* inst = dyn_cast(&*i))
- toRemove.insert(inst);
- }
- for(set::iterator i = toRemove.begin(), e = toRemove.end(); i != e; ++i)
- (*i)->eraseFromParent();
-
- // don't forget to make the array size -1 for uninst (alleviating the
- // need for asprin when looking at debug info)
- new StoreInst(ConstantInt::get(tInt, -1), idxInst, true,
- oldEntry->getFirstNonPHI());
-
- // build the call map for future (call coverage) instrumentation
- this->oldToNewCallMap.clear();
- for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i){
- if(CallInst* theCall = dyn_cast(&*i)){
- if(theCall->getCalledFunction() &&
- theCall->getCalledFunction()->isIntrinsic())
- continue;
- Value* origInst = argsValueMap[valueMap[theCall]];
- if(!origInst){
- // if the value isn't mapped to, it is, itself, a copied value
- // NOTE: we could add another map to check this at run-time every
- // time, if fear arises
- continue;
- }
- Instruction* oldInst = dyn_cast(origInst);
- this->oldToNewCallMap[oldInst] = theCall;
- }
- }
-
- // get rid of the copied function body
- newF->deleteBody();
}
- else{
- // build the call map for future (call coverage) instrumentation
- // note the values will be NULL because there is no body copy
- this->oldToNewCallMap.clear();
- for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i){
- if(CallInst* theCall = dyn_cast(&*i)){
- if(theCall->getCalledFunction() &&
- theCall->getCalledFunction()->isIntrinsic())
- continue;
- this->oldToNewCallMap[theCall] = NULL;
- }
- }
-
- if(!SilentInternal){
- errs() << "WARNING: instrumentation not done for function "
- << F.getName() << " due to large path count. Path info "
- << "will be missing!\n";
- }
- newF->deleteBody();
+ else if(!SilentInternal){
+ errs() << "WARNING: instrumentation not done for function "
+ << F.getName() << " due to large path count. Path info "
+ << "will be missing!\n";
return false;
}
return true;
}
-// Output the bitcode if we want to observe instrumentation changess
-#define PRINT_MODULE dbgs() << \
- "\n\n============= MODULE BEGIN ===============\n" << M << \
- "\n============== MODULE END ================\n"
-
-bool PathTracing::doInitialization(Module& M){
- if(TrackerFile == ""){
- cerr << "ERROR: PT cannot continue. -pt-tracker-file [file] is required.\n";
- exit(1);
- }
- _trackerStream.open(TrackerFile.c_str(), ios::out | ios::trunc);
- if(!_trackerStream.is_open()){
- cerr << "ERROR: unable to open pt-file location: " << TrackerFile << endl;
- exit(2);
- }
- else
- DEBUG(string("Output stream opened to ") + TrackerFile.c_str());
+bool PathTracing::runOnModule(Module& M){
+ static bool runBefore = false;
+ if(runBefore)
+ return(false);
+ runBefore = true;
- Context = &M.getContext();
- Builder = new DIBuilder(M);
+ if(TrackerFile.empty())
+ report_fatal_error("PT cannot continue: -pt-tracker-file [file] is required", false);
+ trackerStream.open(TrackerFile.c_str(), ios::out | ios::trunc);
+ if(!trackerStream.is_open())
+ report_fatal_error("unable to open pt-file location: " + TrackerFile);
+ DEBUG(dbgs() << "Output stream opened to " << TrackerFile << '\n');
+
+ this->Context = &M.getContext();
if(ArraySize > 0)
PATHS_SIZE = ArraySize;
@@ -1230,54 +1103,33 @@ bool PathTracing::doInitialization(Module& M){
else
HASH_THRESHHOLD = ULONG_MAX / 2 - 1;
- string funcsToInst = "";
- bool instAllFuncs = false;
- char* envFuncs = getenv("PT_INST_FUNCS");
- if(!envFuncs || strlen(envFuncs) == 0 || !strcmp(envFuncs, "ALL")){
- instAllFuncs = true;
- }
- else{
- funcsToInst = '|' + string(envFuncs) + '|';
+ bool changed = false;
+ for(Module::iterator i = M.begin(), e = M.end(); i != e; ++i){
+ changed |= runOnFunction(*i);
}
- DEBUG("Instrumenting functions: " + (instAllFuncs ? "ALL" : funcsToInst));
- // insert the globals to decide whether to use the instrumented version of
- // each function or not in special section __PT_func_inst
- Type* tInt = Type::getInt8Ty(*Context);
- for(Module::iterator F = M.begin(), e = M.end(); F != e; ++F){
- if(F->isDeclaration() || F->getName().substr(0, 5).equals("__PT_"))
- continue;
-
- string mName = M.getModuleIdentifier();
- size_t mDot = mName.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890");
- while(mDot != string::npos){
- mName.replace(mDot, 1, "_");
- mDot = mName.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890");
- }
- Twine globalName = "__PT_inst_"+StringRef(mName)+"_"+F->getName();
- GlobalVariable* functionGlobal;
- if(instAllFuncs ||
- funcsToInst.find("|" + F->getName().str() + "|") != string::npos){
- functionGlobal = new GlobalVariable(M, tInt, true,
- GlobalValue::ExternalLinkage, ConstantInt::get(tInt, 1), globalName);
- }
- else{
- functionGlobal = new GlobalVariable(M, tInt, true,
- GlobalValue::ExternalLinkage, ConstantInt::get(tInt, 0), globalName);
- }
- functionGlobal->setSection("__PT_func_inst");
- funcInstMap[F] = functionGlobal;
- }
-
- return true;
+ trackerStream.close();
+ return changed;
}
-bool PathTracing::doFinalization(Module& M){
- (void)M; // suppress warning
-
- // close tracking stream if open
- if(_trackerStream.is_open())
- _trackerStream.close();
-
- return false;
+// Output the bitcode if we want to observe instrumentation changess
+#define PRINT_MODULE dbgs() << \
+ "\n\n============= MODULE BEGIN ===============\n" << M << \
+ "\n============== MODULE END ================\n"
+
+void PathTracing::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired();
+ AU.addPreserved();
+
+ // enforce that PT runs after BBC
+ // this is horribly ugly but now necessary for two reasons:
+ // (1) optimization uses the dominator tree, and PT modifies the CFG
+ // (2) since PT modifies the CFG, the number of BBs changes, and thus so
+ // does the BBC array size
+ // it gets worse: this is the reason that we have to now prevent a pass from
+ // running twice (which always could happen, but never did)
+ // NOTE: if any other requirements are added (e.g. that CC also runs before
+ // PT) a crash ensues. Well played LLVM; well played.
+ AU.addRequired();
+ AU.addPreserved();
}
diff --git a/instrumentor/PathTracing.h b/instrumentor/PathTracing.h
index 85bc67e..aa64370 100644
--- a/instrumentor/PathTracing.h
+++ b/instrumentor/PathTracing.h
@@ -9,7 +9,7 @@
// http://portal.acm.org/citation.cfm?id=243857
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -40,7 +40,6 @@
#include "llvm_proxy/CFG.h"
#include "llvm_proxy/DebugInfo.h"
-#include "llvm_proxy/DIBuilder.h"
#include "llvm_proxy/Instructions.h"
#include
@@ -248,35 +247,27 @@ class BLInstrumentationDag : public PPBallLarusDag {
};
// ---------------------------------------------------------------------------
-// PathTracing is a function pass which instruments basd on Ball/Larus
+// PathTracing is a module pass which instruments based on Ball/Larus
// path profiling
// ---------------------------------------------------------------------------
-class PathTracing : public llvm::FunctionPass {
+class PathTracing : public llvm::ModulePass {
private:
// Current context for multi threading support.
llvm::LLVMContext* Context;
- // The Debug Info Builder for this module
- llvm::DIBuilder* Builder;
- // Mapping functions to their global instrumented flag
- std::map funcInstMap;
// Gets/sets the current path tracker and it's debug info
llvm::Value* getPathTracker();
void setPathTracker(llvm::Value* c);
-
llvm::Value* _pathTracker; // The storage for the current path tracker
- std::ofstream _trackerStream; // The output stream to the tracker file
- // (managed by runOnFunction and written to as
- // we go)
- // Get some context to prep for instrumenting appropriate functions
- bool doInitialization(llvm::Module &M);
+ std::ofstream trackerStream; // The output stream to the tracker file
+ // (managed by runOnFunction and written to as
+ // we go)
- // Analyzes the function for path tracing, duplicates, and instruments
+ // Analyzes and instruments the function for path tracing
bool runOnFunction(llvm::Function &F);
-
- // Finish up by closing any open streams
- bool doFinalization(llvm::Module& M);
+ // Perform module-level tasks, open streams, and instrument each function
+ bool runOnModule(llvm::Module &M);
// Creates an increment constant representing incr.
llvm::ConstantInt* createIncrementConstant(long incr, int bitsize);
@@ -323,16 +314,13 @@ class PathTracing : public llvm::FunctionPass {
public:
static char ID; // Pass identification, replacement for typeid
- PathTracing() : FunctionPass(ID) {}
+ PathTracing() : ModulePass(ID) {}
virtual const char *getPassName() const {
return "Intraprocedural Path Tracing";
}
- // the mapping from old instructions <-> new ones (for the in-function
- // duplication that occurs for inst vs. uninst)
- std::map oldToNewCallMap;
-
+ void getAnalysisUsage(llvm::AnalysisUsage &) const;
};
} // end csi_inst namespace
diff --git a/instrumentor/PrepareCSI.cpp b/instrumentor/PrepareCSI.cpp
new file mode 100644
index 0000000..0884669
--- /dev/null
+++ b/instrumentor/PrepareCSI.cpp
@@ -0,0 +1,386 @@
+//===-------------------------- PrepareCSI.cpp ----------------------------===//
+//
+// This module pass replicates functions to allow multiple possible
+// instrumentation schemes. Note that, presently, this causes enormous code
+// bloat.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "csi-prep"
+
+#include "PrepareCSI.h"
+#include "InstrumentationData.h"
+#include "Utils.hpp"
+
+#include "Versions.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "llvm_proxy/Module.h"
+#include "llvm_proxy/InstIterator.h"
+#include "llvm_proxy/IntrinsicInst.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace csi_inst;
+using namespace llvm;
+using namespace std;
+
+static cl::opt SilentInternal("csi-silent", cl::desc("Silence internal "
+ "warnings. Will still print errors which "
+ "cause CSI to fail."));
+static cl::opt VariantsFile("csi-variants-file", cl::desc("The path to "
+ "the instrumentation variants output file."),
+ cl::value_desc("file_path"));
+static cl::opt NoFilter("csi-no-filter", cl::desc("Do not filter "
+ "instrumentation schemes. All schemes are used "
+ "verbatim for function replication."));
+
+// Register CSI prep as a pass
+char PrepareCSI::ID = 0;
+static RegisterPass X("csi",
+ "Necessary preparation for any CSI instrumentation",
+ false, false);
+
+// Output the bitcode if we want to observe instrumentation changess
+#define PRINT_MODULE dbgs() << \
+ "\n\n============= MODULE BEGIN ===============\n" << M << \
+ "\n============== MODULE END ================\n"
+
+bool PrepareCSI::hasInstrumentationType(const Function &F, const string &type) const {
+#if LLVM_VERSION < 30300
+ const map >::const_iterator found =
+ functionSchemes.find(&F);
+ if (found == functionSchemes.end())
+ return false;
+ else
+ return found->second.count(type);
+#else
+ return F.hasFnAttribute(type);
+#endif
+}
+
+void PrepareCSI::addInstrumentationType(Function &F, const string &type) {
+#if LLVM_VERSION < 30300
+ functionSchemes[&F].insert(type);
+#else
+ F.addFnAttr(type);
+#endif
+}
+
+Function* PrepareCSI::switchIndirect(Function* F, GlobalVariable* switcher,
+ vector& replicas){
+ F->dropAllReferences();
+
+ BasicBlock* newEntry = BasicBlock::Create(*Context, "newEntry", F);
+ vector callArgs;
+ for(Function::arg_iterator k = F->arg_begin(), ke = F->arg_end(); k != ke; ++k)
+ callArgs.push_back(k);
+
+ // set up the switch
+ LoadInst* whichCall = new LoadInst(switcher, "chooseCall", true, newEntry);
+ SwitchInst* callSwitch = NULL;
+
+ // stuff we need
+ IntegerType* tInt = Type::getInt32Ty(*Context);
+
+ // create one bb for each possible call (instrumentation scheme)
+ bool aZero = false;
+ for(unsigned int i = 0; i < replicas.size(); ++i){
+ Function* newF = replicas[i];
+ BasicBlock* bb = BasicBlock::Create(*Context, "call", F);
+ if(callSwitch == NULL){
+ callSwitch = SwitchInst::Create(whichCall, bb, replicas.size(),
+ newEntry);
+ }
+ string funcName = newF->getName().str();
+
+ if(funcName.length() > 5 &&
+ funcName.substr(funcName.length()-5, 5) == "$none"){
+ callSwitch->addCase(ConstantInt::get(tInt, 0), bb);
+ if(aZero)
+ report_fatal_error("multiple defaults for function '" +
+ F->getName() + "'");
+ aZero = true;
+ }
+ else
+ callSwitch->addCase(ConstantInt::get(tInt, i+1), bb);
+
+ CallInst* oneCall = CallInst::Create(newF, callArgs,
+ (F->getReturnType()->isVoidTy()) ? "" : "theCall", bb);
+ oneCall->setTailCall(true);
+ if(F->getReturnType()->isVoidTy())
+ ReturnInst::Create(*Context, bb);
+ else
+ ReturnInst::Create(*Context, oneCall, bb);
+ }
+ // note that we intentionally started numbering the cases from 1 so that the
+ // zero case is reserved for the uninstrumented variant (if there is one)
+ if(!aZero)
+ switcher->setInitializer(ConstantInt::get(tInt, 1));
+
+ return(F);
+}
+
+void printScheme(vector > > >& schemeData){
+ dbgs() << "------Scheme------\n";
+ for(vector > > >::iterator i = schemeData.begin(), e = schemeData.end(); i != e; ++i){
+ pair > > entry = *i;
+ dbgs() << entry.first << ": ";
+ for(set >::iterator j = entry.second.begin(), je = entry.second.end(); j != je; ++j){
+ set jentry = *j;
+ if(j != entry.second.begin())
+ dbgs() << ",";
+ dbgs() << "{";
+ for(set::iterator k = jentry.begin(), ke = jentry.end(); k != ke; ++k){
+ if(k != jentry.begin())
+ dbgs() << ",";
+ dbgs() << *k;
+ }
+ dbgs() << "}";
+ }
+ dbgs() << "\n";
+ }
+ dbgs() << "------------------\n";
+}
+
+static vector split(string s, char delim){
+ vector result;
+
+ stringstream lineStream(s);
+ string entry;
+ while(getline(lineStream, entry, delim)){
+ result.push_back(entry);
+ if(lineStream.fail() || lineStream.eof())
+ break;
+ }
+ if(lineStream.bad())
+ report_fatal_error("internal error encountered parsing scheme input");
+
+ return(result);
+}
+
+static bool patternMatch(const string &text, const string &pattern){
+ return pattern == text || pattern == "*";
+}
+
+static void verifyScheme(const vector > > >& scheme) {
+ for(vector > > >::const_iterator i = scheme.begin(), e = scheme.end(); i != e; ++i){
+ for(set >::const_iterator j = i->second.begin(), je = i->second.end(); j != je; ++j){
+ for(set::const_iterator k = j->begin(), ke = j->end(); k != ke; ++k){
+ if(Instrumentors.count(*k) < 1)
+ report_fatal_error("invalid instrumentor '" + *k + "' in scheme");
+ }
+ }
+ }
+}
+
+static vector > > > readScheme(istream& in){
+ vector lines;
+ string s;
+ while(getline(in, s)){
+ s.erase(remove_if(s.begin(), s.end(), ::isspace), s.end());
+ if(!s.empty())
+ lines.push_back(s);
+ if(in.fail() || in.eof())
+ break;
+ }
+ if(in.bad())
+ report_fatal_error("error encountered reading schema input");
+
+ vector > > > result;
+
+ for(vector::iterator i = lines.begin(), e = lines.end(); i != e; ++i){
+ vector entries = split(*i, ';');
+ if(entries.size() < 2)
+ report_fatal_error("invalid formatting for line '" + *i + "' in instrumentation schema");
+
+ string fnPattern = entries[0];
+ set > schemes;
+ for(vector::iterator j = ++entries.begin(), je = entries.end(); j != je; ++j){
+ string scheme = *j;
+ transform(scheme.begin(), scheme.end(), scheme.begin(), ::toupper);
+ if(scheme[0] != '{' || scheme[scheme.length()-1] != '}')
+ report_fatal_error("invalid formatting for entry '" + scheme + "' in instrumentation schema", false);
+ scheme.erase(0, 1);
+ scheme.erase(scheme.length()-1, 1);
+
+ const vector methods = split(scheme, ',');
+ set methodsSet;
+ for(vector::const_iterator k = methods.begin(), ke = methods.end(); k != ke; ++k){
+ if(!k->empty())
+ methodsSet.insert(*k);
+ }
+
+ schemes.insert(methodsSet);
+ }
+
+ result.push_back(make_pair(fnPattern, schemes));
+ }
+
+ return(result);
+}
+
+// Entry point of the module
+bool PrepareCSI::runOnModule(Module &M){
+ vector > > > schemeData;
+ if(VariantsFile.empty()){
+ outs() << "Reading stdin for instrumentation scheme...\n";
+ schemeData = readScheme(cin);
+ outs() << "Finished reading stdin for scheme\n";
+ }
+ else{
+ ifstream inFile(VariantsFile.c_str(), ios::in);
+ if(!inFile || !inFile.is_open())
+ report_fatal_error("cannot open specified instrumentation scheme file: " +
+ VariantsFile);
+ schemeData = readScheme(inFile);
+ }
+
+ DEBUG(printScheme(schemeData));
+
+ // verify that all passes provided exist
+ DEBUG(dbgs() << "verifying...\n");
+ verifyScheme(schemeData);
+
+ DEBUG(printScheme(schemeData));
+
+ Context = &M.getContext();
+
+ // Find the matching pattern for each function
+ map >*> matches;
+ for(Module::iterator F = M.begin(), E = M.end(); F != E; ++F){
+ if(F->isDeclaration() || F->isIntrinsic())
+ continue;
+ bool found = false;
+ for(vector > > >::iterator i = schemeData.begin(), e = schemeData.end(); i != e; ++i){
+ if(patternMatch(F->getName(), i->first)){
+ matches[F] = &(i->second);
+ found = true;
+ break;
+ }
+ }
+ if(!found){
+ errs() << "WARNING: No scheme match found for function '"
+ << F->getName() << "'. Skipping.\n";
+ continue;
+ }
+ }
+
+ // Filter patterns matched to each function, and replicate
+ for(map >*>::iterator i = matches.begin(), e = matches.end(); i != e; ++i){
+ Function* F = i->first;
+
+ // go through all filters for each possible scheme. if it passes all
+ // filters, make a replica of this function with those tags on it, and add
+ // that replica to the "replicas" set. else, print warning
+ set*> replicas;
+
+ for(set >::iterator j = i->second->begin(), je = i->second->end(); j != je; ++j){
+ bool passed = true;
+ if(!NoFilter){
+ for(vector::const_iterator fi = Filters.begin(), fe = Filters.end(); fi != fe; ++fi){
+ if(!(*fi)(*j, F)){
+ passed = false;
+ break;
+ }
+ }
+ }
+ if(passed)
+ replicas.insert(&*j);
+ else{
+ outs() << "WARNING: filtered out scheme '";
+ for(set::iterator k = j->begin(), ke = j->end(); k != ke; ++k){
+ if(k != j->begin())
+ outs() << ",";
+ outs() << *k;
+ }
+ outs() << "' for function '" << F->getName().str() << "'\n";
+ continue;
+ }
+ }
+
+ switch (replicas.size()) {
+ case 0:
+ continue;
+ case 1: {
+ // instrument the original body (don't replicate and trampoline)
+ const set* scheme = *(replicas.begin());
+ for(set::iterator j = scheme->begin(), je = scheme->end(); j != je; ++j)
+ addInstrumentationType(*F, *j);
+ break;
+ }
+ default:
+ // if the function is variable-argument, currently don't support
+ if(F->isVarArg()){
+ outs() << "WARNING: cannot instrument variable-argument function '"
+ << F->getName() << "'\n";
+ continue;
+ }
+
+ // make a function for each scheme
+ vector funcReplicas;
+ for(set*>::iterator j = replicas.begin(), je = replicas.end(); j != je; ++j){
+ ValueToValueMapTy valueMap;
+ SmallVector returns;
+ Function* newF = CloneFunction(F, valueMap, false, NULL);
+
+ string name = F->getName().str();
+ if((*j)->begin() == (*j)->end())
+ name += "$none";
+ for(set::iterator k = (*j)->begin(), ke = (*j)->end(); k != ke; ++k){
+ name += "$" + *k;
+ addInstrumentationType(*newF, *k);
+ }
+ newF->setName(name);
+
+ // NOTE: this does not preserve function ordering, thus it could
+ // randomly slightly impact performance positively or negatively
+ F->getParent()->getFunctionList().push_back(newF);
+ funcReplicas.push_back(newF);
+ }
+
+ // assign this function a global switcher variable
+ IntegerType* tInt = Type::getInt32Ty(*Context);
+ string globalName = "__CSI_inst_"+getUniqueCFunctionName(*F);
+ const GlobalValue::LinkageTypes linkage =
+ F->hasAvailableExternallyLinkage()
+ ? GlobalValue::WeakAnyLinkage
+ : GlobalValue::ExternalLinkage;
+ GlobalVariable * const functionGlobal =
+ new GlobalVariable(M, tInt, true, linkage,
+ ConstantInt::get(tInt, 0), globalName);
+
+ functionGlobal->setSection("__CSI_func_inst");
+
+ // set up the trampoline call for this function
+ switchIndirect(F, functionGlobal, funcReplicas);
+ }
+ }
+
+ return(true);
+}
diff --git a/instrumentor/PrepareCSI.h b/instrumentor/PrepareCSI.h
new file mode 100644
index 0000000..1299c67
--- /dev/null
+++ b/instrumentor/PrepareCSI.h
@@ -0,0 +1,84 @@
+//===--------------------------- PrepareCSI.h -----------------------------===//
+//
+// This module pass replicates functions to allow multiple possible
+// instrumentation schemes. Note that, presently, this causes enormous code
+// bloat.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_INSTRUMENTATION_H
+#define CSI_INSTRUMENTATION_H
+
+#include
+
+#include "llvm_proxy/CFG.h"
+#include "llvm_proxy/DebugInfo.h"
+#include "llvm_proxy/Instructions.h"
+
+#include
+#include
+#include
+#include
+
+namespace csi_inst {
+
+// ---------------------------------------------------------------------------
+// PrepareCSI is a module pass that analyzes each function, and prepares
+// each for the appropriate types of instrumentation.
+// ---------------------------------------------------------------------------
+class PrepareCSI : public llvm::ModulePass {
+private:
+ // Current context for multi threading support.
+ llvm::LLVMContext* Context;
+
+#if LLVM_VERSION < 30300
+ // instrumentation attributes attached to each function (so instrumentors
+ // know which functions to operate on)
+ std::map > functionSchemes;
+#endif
+
+ // create the trampoline function
+ // NOTE: this returns a function which is now F (but as a trampoline). The
+ // return value may or may not be F, and, in fact, it is possible after this
+ // call that F no longer exists. All future references to F should be changed
+ // to the return value. The function will, however, fix all existing
+ // references to F (in the bitcode).
+ llvm::Function* switchIndirect(llvm::Function* F,
+ llvm::GlobalVariable* switcher,
+ std::vector& replicas);
+
+ // Analyzes all functions, duplicates, and creates the dispatcher
+ bool runOnModule(llvm::Module &M);
+
+public:
+ static char ID; // Pass identification, replacement for typeid
+ PrepareCSI() : ModulePass(ID) {}
+
+ virtual const char *getPassName() const {
+ return "CSI Preparation for Instrumentation";
+ }
+
+ // does the specified function require the specified instrumentation?
+ bool hasInstrumentationType(const llvm::Function &, const std::string &type) const;
+
+private:
+ // mark the specified function as requiring the specified instrumentation
+ void addInstrumentationType(llvm::Function &F, const std::string &type);
+};
+} // end csi_inst namespace
+
+#endif
diff --git a/instrumentor/SConscript b/instrumentor/SConscript
index c2bfa1b..2f870e4 100644
--- a/instrumentor/SConscript
+++ b/instrumentor/SConscript
@@ -1,32 +1,49 @@
-from SCons.Errors import StopError
-
Import('env')
-if(env.get('LLVMBIN', None)):
- env.PrependENVPath('PATH', (
- env['LLVMBIN'],
- ))
-
lenv = env.Clone(
- tools=('llvm',),
- toolpath='.',
- )
+ tools=(
+ 'clang-analyzer',
+ ),
+ toolpath=(
+ '#scons-tools',
+ ),
+ INCPREFIX='-isystem ',
+)
if not lenv.GetOption('help'):
llvm_version = lenv['LLVM_version']
- llvm_require = ["3.1", "3.2", "3.3"]
- if llvm_version not in llvm_require:
- raise StopError('LLVM %s not supported; need version in %s' % (llvm_version, str(llvm_require)))
- lenv.ParseConfig('llvm-config --cxxflags --ldflags')
-
-lenv.AppendUnique(
- LIBS='LLVM-$LLVM_version',
- SHLINKFLAGS='-Wl,--no-undefined',
+ if not '3.1' <= llvm_version < '3.6':
+ from SCons import Warnings
+ class LLVMVersionWarning(Warning): pass
+ Warnings.enableWarningClass(LLVMVersionWarning)
+ Warnings.warn(LLVMVersionWarning, 'untested using LLVM %s; recommend LLVM 3.1 through 3.5.x' % llvm_version)
+ lenv.ParseConfig('$LLVM_CONFIG --cxxflags --ldflags')
+
+ import SCons
+ if lenv._get_major_minor_revision(SCons.__version__) < (2, 1, 0):
+ for flag in lenv['CFLAGS']:
+ if flag.startswith('-std=c++'):
+ lenv.AppendUnique(CXXFLAGS=flag)
+
+lenv.PrependUnique(
+ LIBS='LLVM-$LLVM_version${("", "svn")[LLVM_version == "3.2"]}',
)
if(lenv.get('CXX_ALT', None)):
lenv['CXX'] = env['CXX_ALT']
+matchedGcc = {
+ '3.1': '4.9.0',
+ '3.2': '4.9.0',
+ '3.3': '4.9.0',
+ '3.4': '4.8.2',
+ '3.5': '4.8.3',
+ '3.6': '5.1.0',
+}.get(str(lenv.get('LLVM_version')))
+
+if matchedGcc:
+ lenv.PrependENVPath('PATH', '/s/gcc-%s/bin' % matchedGcc)
+
# extra files to be included in source distributions
File('llvm.py')
@@ -40,9 +57,46 @@ map(Glob, (
########################################################################
-csiInstrumentor = lenv.SharedLibrary(
- "CSI", ("PathNumbering.cpp", "PathTracing.cpp", "CallCoverage.cpp")
-);
+sources = [
+ "BBCoverage.cpp",
+ "CallCoverage.cpp",
+ "CoveragePass.cpp",
+ "ExtrinsicCalls.cpp",
+ "FuncCoverage.cpp",
+ "InfoFileOption.cpp",
+ "InstrumentationData.cpp",
+ "LocalCoveragePass.cpp",
+ "PathNumbering.cpp",
+ "PathTracing.cpp",
+ "PrepareCSI.cpp",
+ "SilentInternalOption.cpp",
+ "Utils.cpp",
+]
+
+csiInstrumentor, = lenv.SharedLibrary('#Release/CSI', sources)
+
+
+########################################################################
+#
+# compilation database for use with various Clang LibTooling tools
+#
+
+
+import json
+
+def compilation_database(env, topdir):
+ for obj in csiInstrumentor.sources:
+ src, = obj.sources
+ yield {
+ 'directory': topdir,
+ 'file': src.path,
+ 'command': env.subst('$SHCXXCOM', target=obj, source=src),
+ }
+
+def stash_compile_commands(target, source, env):
+ topdir, sconscripts = source[0], source[1:]
+ target, = target
+ commands = list(compilation_database(env, topdir.read()))
+ json.dump(commands, open(str(target), 'w'), indent=2)
-installed = Install('#Release', [csiInstrumentor])
-Default(installed)
+lenv.Command('compile_commands.json', (Value(Dir('#').abspath), '#SConstruct', 'SConscript'), stash_compile_commands)
diff --git a/instrumentor/ScopedDIBuilder.h b/instrumentor/ScopedDIBuilder.h
new file mode 100644
index 0000000..a5b8a3f
--- /dev/null
+++ b/instrumentor/ScopedDIBuilder.h
@@ -0,0 +1,43 @@
+//===------------------------- ScopedDIBuilder.h --------------------------===//
+//
+// A debug info builder that automatically finalizes itself when destroyed.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_SCOPED_DI_BUILDER_H
+#define CSI_SCOPED_DI_BUILDER_H
+
+#include "llvm_proxy/DIBuilder.h"
+
+
+namespace csi_inst {
+ class ScopedDIBuilder : public llvm::DIBuilder {
+ public:
+
+#if __cplusplus >= 201103L
+ using DIBuilder::DIBuilder;
+#else
+ // no inherited constructors before C++11
+ ScopedDIBuilder(llvm::Module &module) : DIBuilder(module) { }
+#endif
+
+ ~ScopedDIBuilder() { finalize(); }
+ };
+}
+
+
+#endif
diff --git a/instrumentor/SilentInternalOption.cpp b/instrumentor/SilentInternalOption.cpp
new file mode 100644
index 0000000..7943473
--- /dev/null
+++ b/instrumentor/SilentInternalOption.cpp
@@ -0,0 +1,35 @@
+//===--------------------- SilentInternalOption.cpp -----------------------===//
+//
+// A simple class enacapulating the "silent" flags for each instrumentation
+// pass. Each pass has its own flag to silence warnings, but the front-end
+// just has one command-line for this purpose.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#include "CoveragePassNames.h"
+#include "SilentInternalOption.h"
+
+using namespace llvm;
+using namespace std;
+
+
+csi_inst::SilentInternalOption::SilentInternalOption(const CoveragePassNames &names)
+ : flag(names.lowerShort + "-silent"),
+ description("Silence internal warnings. Will still print errors that cause " + names.upperShort + " to fail."),
+ option(flag.c_str(), cl::desc(description.c_str()))
+{
+}
diff --git a/instrumentor/SilentInternalOption.h b/instrumentor/SilentInternalOption.h
new file mode 100644
index 0000000..24bb4fc
--- /dev/null
+++ b/instrumentor/SilentInternalOption.h
@@ -0,0 +1,59 @@
+//===---------------------- SilentInternalOption.h ------------------------===//
+//
+// A simple class enacapulating the "silent" flags for each instrumentation
+// pass. Each pass has its own flag to silence warnings, but the front-end
+// just has one command-line for this purpose.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+#ifndef CSI_SILENT_INTERNAL_H
+#define CSI_SILENT_INTERNAL_H
+
+#include
+
+#include
+
+
+namespace csi_inst
+{
+ struct CoveragePassNames;
+
+
+ class SilentInternalOption
+ {
+ public:
+ SilentInternalOption(const CoveragePassNames &);
+ operator bool() const;
+
+ private:
+ const std::string flag;
+ const std::string description;
+ llvm::cl::opt option;
+ };
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+inline csi_inst::SilentInternalOption::operator bool() const
+{
+ return option;
+}
+
+
+#endif // !CSI_SILENT_INTERNAL_H
diff --git a/instrumentor/Utils.cpp b/instrumentor/Utils.cpp
new file mode 100644
index 0000000..ee49ba9
--- /dev/null
+++ b/instrumentor/Utils.cpp
@@ -0,0 +1,220 @@
+//===---------------------------- Utils.cpp -------------------------------===//
+//
+// Utilities providing convenience functions for various tasks not specific to
+// any particular instrumentation pass.
+//
+//===----------------------------------------------------------------------===//
+//
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//===----------------------------------------------------------------------===//
+
+#define DEBUG_TYPE "utilities"
+
+#include "Utils.hpp"
+
+#include "llvm_proxy/CFG.h"
+#include "llvm_proxy/Module.h"
+#include "llvm_proxy/DebugInfo.h"
+#include "llvm_proxy/InstIterator.h"
+#include "llvm_proxy/IRBuilder.h"
+
+#include
+#include
+
+#if LLVM_VERSION < 30200
+#include
+#endif
+
+using namespace csi_inst;
+using namespace llvm;
+using namespace std;
+
+
+static string nameMash(string mName){
+ size_t mDot = mName.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890");
+ while(mDot != string::npos){
+ mName.replace(mDot, 1, "_");
+ mDot = mName.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_1234567890");
+ }
+ return(mName);
+}
+
+
+string csi_inst::getUniqueCFunctionName(const Function &F)
+{
+ const DebugLoc dbLoc = findEarlyDebugLoc(F, true);
+
+ if(dbLoc.isUnknown())
+ return nameMash(F.getParent()->getModuleIdentifier()) + '_' +
+ F.getName().str();
+
+ const LLVMContext &context = F.getParent()->getContext();
+ DISubprogram sp = getDISubprogram(dbLoc.getScope(context));
+ if(!sp.isSubprogram())
+ return nameMash(F.getParent()->getModuleIdentifier()) + '_' +
+ F.getName().str();
+
+ return nameMash(sp.getFilename().str()) + '_' + F.getName().str();
+}
+
+
+string csi_inst::setBB_asstring(set theSet){
+ string result;
+ for(set::iterator i = theSet.begin(), e = theSet.end(); i != e; ++i){
+ if(i != theSet.begin())
+ result += ",";
+ if(*i == NULL)
+ result += "NULL";
+ else
+ result += (*i)->getName().str();
+ }
+ return(result);
+}
+
+
+GlobalVariable &csi_inst::getOrCreateGlobal(DIBuilder &debugBuilder, Function &function,
+ Type &type, const DIType &typeInfo,
+ const string &upperShortName)
+{
+ // mangle up a unique name
+ const string fName = getUniqueCFunctionName(function);
+ const string globalName = "__" + upperShortName + "_arr_" +
+ fName.substr(0, ((fName.find("$") == string::npos)
+ ? fName.size()
+ : fName.find("$")));
+
+ // check for existing global
+ Module &module = *function.getParent();
+ GlobalVariable *preexisting = module.getGlobalVariable(globalName, false);
+ if (preexisting)
+ {
+ if (preexisting->getType()->getElementType() != &type)
+ report_fatal_error("unable to get or create coverage global variable for '" + globalName + "' for function '" + function.getName() + "'");
+ else
+ return *preexisting;
+ }
+
+ // create new global
+ const GlobalValue::LinkageTypes linkage = function.hasAvailableExternallyLinkage()
+ ? GlobalValue::WeakAnyLinkage
+ : GlobalValue::ExternalLinkage;
+ Constant * const initializer = Constant::getNullValue(&type);
+ GlobalVariable &result = *new GlobalVariable(module, &type, false, linkage, initializer, globalName);
+ createGlobalVariable(debugBuilder, typeInfo, result);
+ return result;
+}
+
+DIType csi_inst::createArrayType(DIBuilder &builder, uint64_t count, const DIType &elementType)
+{
+ const uint64_t elementSizeInBits = elementType.getSizeInBits();
+#if LLVM_VERSION < 30600
+ Value * const subscript = builder.getOrCreateSubrange(0, count - (LLVM_VERSION < 30300));
+ const DIArray subscriptArray = builder.getOrCreateArray(subscript);
+#else
+ Metadata * const subscript[] = { builder.getOrCreateSubrange(0, count) };
+ const DIArray subscriptArray = builder.getOrCreateArray(subscript);
+#endif
+ return builder.createArrayType(count * elementSizeInBits, elementSizeInBits, elementType, subscriptArray);
+}
+
+
+DIGlobalVariable csi_inst::createGlobalVariable(DIBuilder &builder, const DIType &typeInfo, GlobalVariable &global)
+{
+ const StringRef name = global.getName();
+#if LLVM_VERSION < 30600
+ return builder.createGlobalVariable(name, DIFile(), 0, typeInfo, false, &global);
+#else
+ return builder.createGlobalVariable(DIFile(), name, name, DIFile(), 0, typeInfo, false, &global);
+#endif
+}
+
+
+DebugLoc csi_inst::findEarlyDebugLoc(const Function &function, bool silent)
+{
+ // Iterate over all instructions. First instructions seen will be
+ // those from the entry block, which is where we want to find a
+ // debug location if possible. If we don't find one there, though,
+ // then we'll simply continue on to instructions from other basic
+ // blocks.
+
+ for (const_inst_iterator instruction = inst_begin(function), end = inst_end(function); instruction != end; ++instruction)
+ {
+ const DebugLoc &location = instruction->getDebugLoc();
+ if (!location.isUnknown())
+ {
+ if (!silent && instruction->getParent() != &function.getEntryBlock())
+ DEBUG(dbgs() << "WARNING: debug location outside of entry block used "
+ << "for instrumented function " << function.getName() << '\n');
+ return location;
+ }
+ }
+
+ if (!silent)
+ errs() << "WARNING: there will be no debug locations for instrumented"
+ << " function " << function.getName() << '\n';
+ return DebugLoc();
+}
+
+
+Instruction *csi_inst::insertDeclare(DIBuilder &builder, Value *var, DIVariable varInfo, const DebugLoc &location, Instruction *before)
+{
+#if LLVM_VERSION < 30600
+ Instruction * const declaration = builder.insertDeclare(var, varInfo, before);
+#else
+ Instruction * const declaration = builder.insertDeclare(var, varInfo, builder.createExpression(), before);
+#endif
+ declaration->setDebugLoc(location);
+ return declaration;
+}
+
+
+static uint64_t getTypeStoreSize(const Module &module, Type &type)
+{
+#if LLVM_VERSION < 30200
+ return TargetData(&module).getTypeStoreSize(&type);
+#elif LLVM_VERSION < 30500
+ return DataLayout(&module).getTypeStoreSize(&type);
+#else
+ return module.getDataLayout()->getTypeStoreSize(&type);
+#endif
+}
+
+
+AllocaInst *csi_inst::createZeroedLocalArray(Function &function, ArrayType &arrayType, const string &name, DIBuilder &debugBuilder, const DIType &elementTypeInfo, bool silent)
+{
+ // find proper insertion point for new alloca and other supporting instructions
+ const BasicBlock::iterator entryInst = function.getEntryBlock().getFirstInsertionPt();
+ IRBuilder<> builder(entryInst);
+
+ // allocate stack space for array and zero-initialize all elements
+ const Module &module = *function.getParent();
+ AllocaInst * const arrayAllocation = builder.CreateAlloca(&arrayType, NULL, name);
+ const uint64_t sizeInBytes = getTypeStoreSize(module, arrayType);
+ builder.CreateMemSet(arrayAllocation, builder.getInt8(0), sizeInBytes, 0, true);
+
+ // set up debug metadata
+ const DebugLoc &location = findEarlyDebugLoc(function, silent);
+ if (!location.isUnknown()) {
+ const uint64_t elementCount = arrayType.getNumElements();
+ const DIType arrayTypeInfo = createArrayType(debugBuilder, elementCount, elementTypeInfo);
+ const LLVMContext &context = module.getContext();
+ const MDNode * const scope = location.getScope(context);
+ const DIVariable arrayVarInfo = debugBuilder.createLocalVariable(dwarf::DW_TAG_auto_variable, DIDescriptor(scope), name, DIFile(scope), 0, arrayTypeInfo);
+ insertDeclare(debugBuilder, arrayAllocation, arrayVarInfo, location, entryInst);
+ }
+
+ // return allocated array to caller for further use
+ return arrayAllocation;
+}
diff --git a/instrumentor/Utils.hpp b/instrumentor/Utils.hpp
index 6047d1e..89d5e1e 100644
--- a/instrumentor/Utils.hpp
+++ b/instrumentor/Utils.hpp
@@ -1,11 +1,11 @@
//===---------------------------- Utils.hpp -------------------------------===//
//
-// This pass instruments function calls for interprocedural analysis by
-// gathering both global and local coverage information.
+// Utilities providing convenience functions for various tasks not specific to
+// any particular instrumentation pass.
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -24,6 +24,18 @@
#define CSI_UTILS
#include "llvm_proxy/BasicBlock.h"
+#include "llvm_proxy/DIBuilder.h"
+#include "llvm_proxy/Module.h"
+
+#include
+#include
+
+namespace llvm {
+ class AllocaInst;
+ class ArrayType;
+}
+
+namespace csi_inst {
inline llvm::BasicBlock::iterator nextInst(llvm::Instruction* const instruction){
return ++llvm::BasicBlock::iterator(instruction);
@@ -34,5 +46,40 @@ inline llvm::BasicBlock::iterator prevInst(llvm::Instruction* const instruction)
return --llvm::BasicBlock::iterator(instruction);
}
+ // get a unique name for a C function, F (utilizes function name and filename,
+ // where possible)
+ std::string getUniqueCFunctionName(const llvm::Function &F);
+
+ // debugging routine to translate a set of basic blocks into a printable string
+ std::string setBB_asstring(std::set theSet);
+
+ // get or create a global variable with the specified parameters
+ llvm::GlobalVariable &getOrCreateGlobal(llvm::DIBuilder &, llvm::Function &,
+ llvm::Type &, const llvm::DIType &,
+ const std::string &upperShortName);
+
+ llvm::DIType createArrayType(llvm::DIBuilder &, uint64_t count, const llvm::DIType &elementType);
+
+ llvm::DIGlobalVariable createGlobalVariable(llvm::DIBuilder &, const llvm::DIType &, llvm::GlobalVariable &);
+
+ llvm::DebugLoc findEarlyDebugLoc(const llvm::Function &, bool silent);
+
+ llvm::Instruction *insertDeclare(llvm::DIBuilder &, llvm::Value *, llvm::DIVariable, const llvm::DebugLoc &, llvm::Instruction *);
+
+ llvm::AllocaInst *createZeroedLocalArray(llvm::Function &, llvm::ArrayType &, const std::string &name, llvm::DIBuilder &, const llvm::DIType &, bool);
+
+} // end csi_inst namespace
+
+#if LLVM_VERSION < 30300
+namespace llvm {
+ // the crash diagnostic parameter does not exist prior to LLVM 3.3
+ // (we just discard this parameter in earlier LLVM versions and call
+ // down to llvm's report_fatal_error)
+ inline void report_fatal_error(const llvm::Twine &Reason, bool)
+ {
+ report_fatal_error(Reason);
+ }
+}
+#endif
#endif
diff --git a/instrumentor/Versions.h b/instrumentor/Versions.h
index 6897a3e..7b9cab4 100644
--- a/instrumentor/Versions.h
+++ b/instrumentor/Versions.h
@@ -4,7 +4,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,17 +22,16 @@
#ifndef CSI_VERSIONS_H
#define CSI_VERSIONS_H
-#include
-#if !defined(LLVM_VERSION_MAJOR) || LLVM_VERSION_MAJOR != 3 || \
- !defined(LLVM_VERSION_MINOR) || (LLVM_VERSION_MINOR != 1 && \
- LLVM_VERSION_MINOR != 2 && \
- LLVM_VERSION_MINOR != 3)
- #error "LLVM version not supported. Supported versions are <3.1, 3.2, 3.3>"
+#include
+
+#ifndef LLVM_VERSION_PATCH
+# define LLVM_VERSION_PATCH 0
#endif
-#define LLVM_3_1 (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 1)
-#define LLVM_3_2 (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 2)
-#define LLVM_3_3 (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR == 3)
+
+#define LLVM_VERSION (LLVM_VERSION_MAJOR * 10000 \
+ + LLVM_VERSION_MINOR * 100 \
+ + LLVM_VERSION_PATCH)
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
diff --git a/instrumentor/llvm.py b/instrumentor/llvm.py
index 818efad..b12c4bd 100644
--- a/instrumentor/llvm.py
+++ b/instrumentor/llvm.py
@@ -23,9 +23,17 @@
from sys import stderr
+def check_llvm_bindir(context):
+ context.Message('Checking for LLVM binaries...')
+ success, output = context.TryAction('$LLVM_CONFIG --bindir >$TARGET')
+ output = output.rstrip()
+ context.Result(str(output))
+ context.env['LLVM_bindir'] = output
+
+
def check_llvm_version(context):
context.Message('Checking for LLVM version...')
- success, output = context.TryAction('llvm-config --version >$TARGET')
+ success, output = context.TryAction('$LLVM_CONFIG --version >$TARGET')
# some releases of LLVM 3.2 shipped as version 3.2svn
output = output.replace('svn', '')
if(not output):
@@ -43,11 +51,13 @@ def generate(env):
conf = Configure(
env,
custom_tests={
+ 'CheckLLVMBinDir': check_llvm_bindir,
'CheckLLVMVersion': check_llvm_version,
},
help=False,
)
conf.CheckLLVMVersion()
+ conf.CheckLLVMBinDir()
conf.Finish()
diff --git a/instrumentor/llvm_proxy/BasicBlock.h b/instrumentor/llvm_proxy/BasicBlock.h
index 0ba946b..6414ad2 100644
--- a/instrumentor/llvm_proxy/BasicBlock.h
+++ b/instrumentor/llvm_proxy/BasicBlock.h
@@ -4,7 +4,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,8 +20,8 @@
//===----------------------------------------------------------------------===//
#include "../Versions.h"
-#if LLVM_3_1 || LLVM_3_2
+#if LLVM_VERSION < 30300
#include
-#elif LLVM_3_3
+#else
#include
#endif
diff --git a/instrumentor/llvm_proxy/CFG.h b/instrumentor/llvm_proxy/CFG.h
index 5d034a4..657f6eb 100644
--- a/instrumentor/llvm_proxy/CFG.h
+++ b/instrumentor/llvm_proxy/CFG.h
@@ -4,7 +4,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,19 +20,8 @@
//===----------------------------------------------------------------------===//
#include "../Versions.h"
-#if GCC_VERSION > 40600 || defined(__clang__)
- // GCC 4.6+ / clang
- #pragma GCC diagnostic push
-#endif
-
-#if GCC_VERSION > 40200 || defined(__clang__)
- // GCC 4.2+ / clang
- #pragma GCC diagnostic ignored "-Wunused-parameter"
-#endif
-
-#include
-
-#if GCC_VERSION > 40600 || defined(__clang__)
- // GCC 4.6+ / clang
- #pragma GCC diagnostic pop
+#if LLVM_VERSION < 30500
+ #include
+#else
+ #include
#endif
diff --git a/instrumentor/llvm_proxy/DIBuilder.h b/instrumentor/llvm_proxy/DIBuilder.h
index 2b98525..2f0c1b9 100644
--- a/instrumentor/llvm_proxy/DIBuilder.h
+++ b/instrumentor/llvm_proxy/DIBuilder.h
@@ -4,7 +4,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Copyright (c) 2013 Peter J. Ohmann and Benjamin R. Liblit
+// Copyright (c) 2016 Peter J. Ohmann and Benjamin R. Liblit
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -20,8 +20,10 @@
//===----------------------------------------------------------------------===//
#include "../Versions.h"
-#if LLVM_3_1
+#if LLVM_VERSION < 30200
#include