Code Coverage Support (#46)

* Code-coverage support

* Examples update

- code-coverage example
- tweak transpose and parallel-overhead

* Coverage output + testing

- config::get_setting value(...)
- REGULAR_EXPRESSION -> REGEX in cmake func args
- coverage.hpp header
- coverage JSON
- coverage tests

* cmake formatting

* Library instrumentation w/o main + more

- fixed library instrumentation w/o main
- use TIMEMORY_PROJECT_NAME in output messages
- removed '--driver' option from omnitrace exe
- support coverage in trace mode
- OMNITRACE_KOKKOS_KERNEL_LOGGER
- support multiple calls to omnitrace_set_env after init if already called
- support multiple calls to omnitrace_set_mpi after init if same args
- support multiple calls to omnitrace_init if same mode
- unique_ptr_t for thread_data which calls finalize when thread_data is destroyed
- tweaked openmp tests
- improved finalization

* Replace CI --output-on-failure with -V

* Fix to OMNITRACE_DL_INVOKE

* omnitrace-exe and testing updates

- omnitrace::omnitrace-timemory interface library
- support for configs in omnitrace exe
- print-{available,instrumented,...} opts no longer exit w/o --simulate
- all tests apply --print-instrumented functions
- tweaked coverage tests
- print-* options print instructions not address range

* Remove OMNITRACE_DEBUG_FINALIZE=ON from CI

* Python cmake tweaks

* Tweak test ordering

* Upload CI artifacts if fail or success

* CI Python tweaks

- Use OMNITRACE_PYTHON_PREFIX and OMNITRACE_PYTHON_ENVS

* CI ELFULTILS_DOWNLOAD_VERSION

* test tweaks

- labels and more coverage tests

* tweak to omnitrace --config handling

* Update module/function constraint handling + PP

- tweak pre-processor definition handling
- removed free-standing module_constraint
- remove free-standing routine_constraint
- remove module_name.find("omnitrace") module constraint
- fully handle the output path of omnitrace *-instr files
- get_use_code_coverage config option
- print-coverage option
- coverage_module_functions

* use github.job not github.name

* Re-enable HSA_ENABLE_INTERRUPT

- remove coverage address report

[ROCm/rocprofiler-systems commit: 791375bb24]
This commit is contained in:
Jonathan R. Madsen
2022-04-25 17:00:52 -05:00
committed by GitHub
parent 28ade7fbb9
commit 72d0a7d08a
58 changed files with 2572 additions and 794 deletions
@@ -22,9 +22,9 @@ target_link_libraries(
omnitrace-exe
PRIVATE omnitrace::omnitrace-headers
omnitrace::omnitrace-dyninst
omnitrace::omnitrace-timemory
omnitrace::omnitrace-compile-options
omnitrace::omnitrace-compile-definitions
$<BUILD_INTERFACE:timemory::timemory-headers>
$<IF:$<BOOL:${OMNITRACE_USE_SANITIZER}>,omnitrace::omnitrace-sanitizer,>)
set_target_properties(
@@ -20,6 +20,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "function_signature.hpp"
#include "fwd.hpp"
#include "omnitrace.hpp"
static int expect_error = NO_ERROR;
@@ -673,11 +675,12 @@ get_func_file_line_info(module_t* module, procedure_t* func)
std::vector<string_t> _params = {};
if(params)
{
_params.reserve(params->size());
for(auto* itr : *params)
{
string_t _name = itr->getType()->getName();
if(_name.empty()) _name = itr->getName();
_params.push_back(_name);
_params.emplace_back(_name);
}
}
@@ -699,6 +702,136 @@ get_func_file_line_info(module_t* module, procedure_t* func)
}
}
//======================================================================================//
//
// Gets information (line number, filename, and column number) about
// the instrumented loop and formats it properly.
//
std::map<basic_block_t*, basic_block_signature>
get_basic_block_file_line_info(module_t* module, procedure_t* func)
{
std::map<basic_block_t*, basic_block_signature> _data{};
if(!func) return _data;
auto* _cfg = func->getCFG();
std::set<BPatch_basicBlock*> _basic_blocks{};
_cfg->getAllBasicBlocks(_basic_blocks);
if(_basic_blocks.empty()) return _data;
char fname[FUNCNAMELEN + 1];
char mname[FUNCNAMELEN + 1];
std::string typeName = {};
memset(fname, '\0', FUNCNAMELEN + 1);
memset(mname, '\0', FUNCNAMELEN + 1);
module->getName(mname, FUNCNAMELEN);
func->getName(fname, FUNCNAMELEN);
auto* returnType = func->getReturnType();
if(returnType) typeName = returnType->getName();
auto* params = func->getParams();
std::vector<string_t> _params;
if(params)
{
for(auto* itr : *params)
{
string_t _name = itr->getType()->getName();
if(_name.empty()) _name = itr->getName();
_params.push_back(_name);
}
}
for(auto&& itr : _basic_blocks)
{
auto base_addr = itr->getStartAddress();
auto last_addr = itr->getEndAddress();
verbprintf(4, "BB: size = %lu: base_addr = %lu, last_addr = %lu\n",
(unsigned long) (last_addr - base_addr), base_addr, last_addr);
bpvector_t<BPatch_statement> linesBeg{};
bpvector_t<BPatch_statement> linesEnd{};
string_t filename = mname;
if(module->getSourceLines(base_addr, linesBeg) && !linesBeg.empty())
{
int row1 = linesBeg.front().lineNumber();
int col1 = linesBeg.front().lineOffset();
verbprintf(4, "size of linesEnd = %lu\n", (unsigned long) linesEnd.size());
if(module->getSourceLines(last_addr, linesEnd) && !linesEnd.empty())
{
int row2 = linesEnd.back().lineNumber();
int col2 = linesEnd.back().lineOffset();
if(row2 < row1) std::swap(row1, row2);
if(row1 == row2 && col2 < col1) std::swap(col1, col2);
_data.emplace(itr,
basic_block_signature{
base_addr, last_addr,
function_signature(typeName, fname, filename, _params,
{ row1, row2 }, { col1, col2 }, true,
true, true) });
}
else
{
_data.emplace(itr,
basic_block_signature{
base_addr, last_addr,
function_signature(typeName, fname, filename, _params,
{ row1, 0 }, { col1, 0 }, true, true,
false) });
}
}
else
{
_data.emplace(itr,
basic_block_signature{
base_addr, last_addr,
function_signature(typeName, fname, filename, _params) });
}
}
return _data;
}
//======================================================================================//
//
// We create a new name that embeds the file and line information in the name
//
std::vector<statement_t>
get_source_code(module_t* module, procedure_t* func)
{
std::vector<statement_t> _lines{};
if(!module || !func) return _lines;
auto* _cfg = func->getCFG();
std::set<BPatch_basicBlock*> _basic_blocks{};
_cfg->getAllBasicBlocks(_basic_blocks);
for(auto&& itr : _basic_blocks)
{
auto _base_addr = itr->getStartAddress();
auto _last_addr = itr->getEndAddress();
for(decltype(_base_addr) _addr = _base_addr; _addr <= _last_addr; ++_addr)
{
std::vector<statement_t> _src{};
if(module->getSourceLines(_addr, _src))
{
for(auto&& iitr : _src)
_lines.emplace_back(iitr);
}
}
}
return _lines;
}
//======================================================================================//
//
// Error callback routine.
@@ -59,12 +59,14 @@ function_signature::get(function_signature& sig)
}
string_t
function_signature::get() const
function_signature::get(bool _all, bool _save) const
{
if(!_all && _save && !m_signature.empty()) return m_signature;
std::stringstream ss;
if(use_return_info && !m_return.empty()) ss << m_return << " ";
if((_all || use_return_info) && !m_return.empty()) ss << m_return << " ";
ss << m_name;
if(use_args_info) ss << m_params;
if(_all || use_args_info) ss << m_params;
if(m_loop && m_info_beg)
{
auto _row_col_str = [](unsigned long _row, unsigned long _col) {
@@ -90,10 +92,52 @@ function_signature::get() const
else
errprintf(1, "loop line info is empty!");
}
if(use_file_info && m_file.length() > 0) ss << " [" << m_file;
if(use_line_info && m_row.first > 0) ss << ":" << m_row.first;
if(use_file_info && m_file.length() > 0) ss << "]";
if((_all || use_file_info) && m_file.length() > 0) ss << " [" << m_file;
if((_all || use_line_info) && m_row.first > 0) ss << ":" << m_row.first;
if((_all || use_file_info) && m_file.length() > 0) ss << "]";
m_signature = ss.str();
return m_signature;
if(_save) m_signature = ss.str();
return ss.str();
}
string_t
function_signature::get_coverage(bool _basic_block) const
{
std::stringstream ss;
if(!m_return.empty()) ss << m_return << " ";
ss << m_name << m_params;
if(_basic_block && m_loop && m_info_beg)
{
if(m_file.length() > 0) ss << " [" << m_file << "]";
auto _row_col_str = [](unsigned long _row, unsigned long _col) {
std::stringstream _ss{};
if(_row == 0 && _col == 0) return std::string{};
if(_col > 0)
_ss << "{" << _row << "," << _col << "}";
else
_ss << "{" << _row << "}";
return _ss.str();
};
auto _rc1 = _row_col_str(m_row.first, m_col.first);
auto _rc2 = _row_col_str(m_row.second, m_col.second);
if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 != _rc2)
ss << " [" << _rc1 << "-" << _rc2 << "]";
else if(m_info_end && !_rc1.empty() && !_rc2.empty() && _rc1 == _rc2)
ss << " [" << _rc1 << "]";
else if(m_info_end && !_rc1.empty() && _rc2.empty())
ss << " [" << _rc1 << "]";
else if(!m_info_end && !_rc1.empty())
ss << " [" << _rc1 << "]";
else
errprintf(1, "loop line info is empty!");
}
else
{
if(m_file.length() > 0) ss << " [" << m_file;
if(m_row.first > 0) ss << ":" << m_row.first;
if(m_file.length() > 0) ss << "]";
}
return ss.str();
}
@@ -41,7 +41,8 @@ struct function_signature
bool _info_beg = false, bool _info_end = false);
static string_t get(function_signature& sig);
string_t get() const;
string_t get(bool _all = false, bool _save = true) const;
string_t get_coverage(bool _is_basic_block) const;
bool m_loop = false;
bool m_info_beg = false;
@@ -72,3 +73,12 @@ struct function_signature
(void) get();
}
};
struct basic_block_signature
{
using address_t = Dyninst::Address;
address_t start_address = {};
address_t last_address = {};
function_signature signature = {};
};
@@ -22,7 +22,6 @@
#pragma once
#include <string_view>
#include <timemory/backends/process.hpp>
#include <timemory/environment.hpp>
#include <timemory/mpl/apply.hpp>
@@ -65,6 +64,7 @@
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unistd.h>
#include <vector>
@@ -78,6 +78,7 @@
#endif
struct function_signature;
struct basic_block_signature;
struct module_function;
template <typename Tp>
@@ -105,6 +106,7 @@ using snippet_t = BPatch_snippet;
using call_expr_t = BPatch_funcCallExpr;
using address_space_t = BPatch_addressSpace;
using flow_graph_t = BPatch_flowGraph;
using statement_t = BPatch_statement;
using basic_block_t = BPatch_basicBlock;
using basic_loop_t = BPatch_basicBlockLoop;
using procedure_loc_t = BPatch_procedureLocation;
@@ -125,6 +127,13 @@ using instruction_t = Dyninst::InstructionAPI::Instruction;
void
omnitrace_prefork_callback(thread_t* parent, thread_t* child);
enum CodeCoverageMode
{
CODECOV_NONE = 0,
CODECOV_FUNCTION,
CODECOV_BASIC_BLOCK
};
//======================================================================================//
//
// Global Variables
@@ -167,21 +176,22 @@ extern string_t prefer_library;
//
// global variables
//
extern patch_pointer_t bpatch;
extern call_expr_t* terminate_expr;
extern snippet_vec_t init_names;
extern snippet_vec_t fini_names;
extern fmodset_t available_module_functions;
extern fmodset_t instrumented_module_functions;
extern fmodset_t overlapping_module_functions;
extern fmodset_t excluded_module_functions;
extern fixed_modset_t fixed_module_functions;
extern regexvec_t func_include;
extern regexvec_t func_exclude;
extern regexvec_t file_include;
extern regexvec_t file_exclude;
extern regexvec_t file_restrict;
extern regexvec_t func_restrict;
extern patch_pointer_t bpatch;
extern call_expr_t* terminate_expr;
extern snippet_vec_t init_names;
extern snippet_vec_t fini_names;
extern fmodset_t available_module_functions;
extern fmodset_t instrumented_module_functions;
extern fmodset_t overlapping_module_functions;
extern fmodset_t excluded_module_functions;
extern fixed_modset_t fixed_module_functions;
extern regexvec_t func_include;
extern regexvec_t func_exclude;
extern regexvec_t file_include;
extern regexvec_t file_exclude;
extern regexvec_t file_restrict;
extern regexvec_t func_restrict;
extern CodeCoverageMode coverage_mode;
//
//======================================================================================//
@@ -230,10 +240,8 @@ consume_parameters(T&&...)
extern "C"
{
bool are_file_include_exclude_lists_empty();
bool instrument_module(const string_t& file_name);
bool instrument_entity(const string_t& function_name);
bool module_constraint(string_view_t fname);
bool routine_constraint(string_view_t fname);
bool module_constraint(const char* fname);
bool routine_constraint(const char* fname);
}
//======================================================================================//
@@ -248,6 +256,12 @@ function_signature
get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph,
basic_loop_t* loopToInstrument);
std::map<basic_block_t*, basic_block_signature>
get_basic_block_file_line_info(module_t* module, procedure_t* func);
std::vector<statement_t>
get_source_code(module_t* module, procedure_t* func);
std::tuple<size_t, size_t>
query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc,
flow_graph_t* cfGraph = nullptr, basic_loop_t* loopToInstrument = nullptr);
@@ -256,11 +270,21 @@ bool
query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cfGraph,
basic_loop_t* loopToInstrument, bool allow_traps);
template <typename Tp>
bool
insert_instr(address_space_t* mutatee, const bpvector_t<point_t*>& _points, Tp traceFunc,
procedure_loc_t traceLoc, bool allow_traps = instr_traps);
template <typename Tp>
bool
insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr,
basic_loop_t* loopToInstrument = nullptr, bool allow_traps = true);
basic_loop_t* loopToInstrument = nullptr, bool allow_traps = instr_traps);
template <typename Tp>
bool
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
basic_block_t* basicBlock, bool allow_traps = instr_traps);
void
errorFunc(error_level_t level, int num, const char** params);
@@ -25,6 +25,12 @@
#include "fwd.hpp"
#include "module_function.hpp"
#include <timemory/mpl/policy.hpp>
#include <timemory/settings.hpp>
#include <timemory/tpls/cereal/cereal.hpp>
#include <timemory/utility/delimit.hpp>
#include <timemory/utility/filepath.hpp>
static inline void
dump_info(std::ostream& _os, const fmodset_t& _data)
{
@@ -54,7 +60,7 @@ dump_info(const string_t& _label, string_t _oname, const string_t& _ext,
namespace cereal = tim::cereal;
namespace policy = tim::policy;
_oname += "." + _ext;
_oname = tim::settings::compose_output_filename(_oname, _ext);
auto _handle_error = [&]() {
std::stringstream _msg{};
_msg << "[dump_info] Error opening '" << _oname << " for output";
@@ -114,16 +114,28 @@ module_function::write_header(std::ostream& os)
bool
module_function::should_instrument() const
{
return should_instrument(false);
}
bool
module_function::should_coverage_instrument() const
{
return should_instrument(true);
}
bool
module_function::should_instrument(bool coverage) const
{
// hard constraints
if(!is_instrumentable()) return false;
if(!can_instrument_entry()) return false;
if(!can_instrument_exit()) return false;
if(!coverage && !can_instrument_exit()) return false;
if(is_module_constrained()) return false;
if(is_routine_constrained()) return false;
// should be before user selection
constexpr int absolute_min_instructions = 4;
constexpr int absolute_min_instructions = 2;
if(num_instructions < absolute_min_instructions)
{
messages.emplace_back(
@@ -134,8 +146,6 @@ module_function::should_instrument() const
// user selection
if(is_user_excluded()) return false;
if(is_user_restricted()) return true;
if(is_user_included()) return true;
// should be applied before dynamic-callsite check
if(is_overlapping_constrained()) return false;
@@ -145,6 +155,10 @@ module_function::should_instrument() const
// needs to be applied before address range and number of instruction constraints
if(is_dynamic_callsite_forced()) return true;
// user selection
if(!file_restrict.empty() || !func_restrict.empty()) return !is_user_restricted();
if(is_user_included()) return true;
if(is_address_range_constrained()) return false;
if(is_num_instructions_constrained()) return false;
@@ -194,7 +208,7 @@ module_function::is_user_restricted() const
if(!func_restrict.empty())
{
if(check_regex_restrictions(module_name, func_restrict))
if(check_regex_restrictions(function_name, func_restrict))
{
messages.emplace_back(2, "Forcing", "function", "function-restrict-regex");
return false;
@@ -282,23 +296,134 @@ module_function::is_overlapping() const
bool
module_function::is_module_constrained() const
{
if(!instrument_module(module_name) || module_constraint(module_name.c_str()))
{
messages.emplace_back(2, "Skipping", "module", "module-constraint");
auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize;
auto _report = [&](const string_t& _action, const string_t& _reason, int _lvl) {
messages.emplace_back(_lvl, _action, "module", _reason);
return true;
}
};
if(module_constraint(function_name.c_str())) return true;
// always instrument these modules
if(module_name == "DEFAULT_MODULE" || module_name == "LIBRARY_MODULE")
return _report("Skipping", "default module", 2);
static std::regex ext_regex{ "\\.(s|S)$", regex_opts };
static std::regex sys_regex{ "^(s|k|e|w)_[A-Za-z_0-9\\-]+\\.(c|C)$", regex_opts };
static std::regex sys_build_regex{ "^(\\.\\./sysdeps/|/build/)", regex_opts };
static std::regex dyninst_regex{ "(dyninst|DYNINST|(^|/)RT[[:graph:]]+\\.c$)",
regex_opts };
static std::regex dependlib_regex{ "^(lib|)(omnitrace|pthread|caliper|gotcha|papi|"
"cupti|TAU|likwid|pfm|nvperf|unwind)",
regex_opts };
static std::regex core_cmod_regex{
"^(malloc|(f|)lock|sig|sem)[a-z_]+(|64|_r|_l)\\.c$"
};
static std::regex core_lib_regex{
"^(lib|)(c|dl|dw|pthread|tcmalloc|profiler|"
"tbbmalloc|tbbmalloc_proxy|malloc|stdc\\+\\+)(-|\\.)",
regex_opts
};
static std::regex prefix_regex{ "^(_|\\.[a-zA-Z0-9])", regex_opts };
// file extensions that should not be instrumented
if(std::regex_search(module_name, ext_regex))
return _report("Excluding", "file extension", 3);
// system modules that should not be instrumented (wastes time)
if(std::regex_search(module_name, sys_regex) ||
std::regex_search(module_name, sys_build_regex))
return _report("Excluding", "system module", 3);
// dyninst modules that must not be instrumented
if(std::regex_search(module_name, dyninst_regex))
return _report("Excluding", "dyninst module", 3);
// modules used by omnitrace and dependent libraries
if(std::regex_search(module_name, core_lib_regex) ||
std::regex_search(module_name, core_cmod_regex))
return _report("Excluding", "core module", 3);
// modules used by omnitrace and dependent libraries
if(std::regex_search(module_name, dependlib_regex))
return _report("Excluding", "dependency module", 3);
// known set of modules whose starting sequence of characters suggest it should not be
// instrumented (wastes time)
if(std::regex_search(module_name, prefix_regex))
return _report("Excluding", "prefix match", 3);
return false;
}
bool
module_function::is_routine_constrained() const
{
if(!instrument_entity(function_name) || !instrument_entity(signature.get()) ||
routine_constraint(function_name) || routine_constraint(signature.get()))
{
messages.emplace_back(2, "Skipping", "function", "function-constraint");
auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize;
auto _report = [&](const string_t& _action, const string_t& _reason, int _lvl) {
messages.emplace_back(_lvl, _action, "function", _reason);
return true;
};
if(routine_constraint(function_name.c_str())) return true;
auto npos = std::string::npos;
if(function_name.find("omnitrace") != npos)
{
return _report("Skipping", "omnitrace-function", 1);
}
if(function_name.find("FunctionInfo") != npos ||
function_name.find("_L_lock") != npos || function_name.find("_L_unlock") != npos)
{
return _report("Skipping", "function-constraint", 2);
}
static std::regex exclude(
"(omnitrace|tim::|N3tim|MPI_Init|MPI_Finalize|dyninst|tm_clones)", regex_opts);
static std::regex exclude_cxx(
"(std::_Sp_counted_base|std::(use|has)_facet|std::locale|::sentry|^std::_|::_(M|"
"S)_|::basic_string[a-zA-Z,<>: ]+::_M_create|::__|::_(Alloc|State)|"
"std::(basic_|)(ifstream|ios|istream|ostream|stream))",
regex_opts);
static std::regex leading("^(_|\\.|frame_dummy|transaction clone|virtual "
"thunk|non-virtual thunk|\\(|targ|kmp_threadprivate_)",
regex_opts);
static std::regex trailing(
"(_|\\.part\\.[0-9]+|\\.constprop\\.[0-9]+|\\.|\\.[0-9]+)$", regex_opts);
static strset_t whole = []() {
auto _v = get_whole_function_names();
auto _ret = _v;
for(std::string _ext : { "64", "_l", "_r" })
for(const auto& itr : _v)
_ret.emplace(itr + _ext);
return _ret;
}();
// don't instrument the functions when key is found anywhere in function name
if(std::regex_search(function_name, exclude) ||
std::regex_search(function_name, exclude_cxx))
{
return _report("Excluding", "critical", 3);
}
if(whole.count(function_name) > 0)
{
return _report("Excluding", "critical-whole-match", 3);
}
// don't instrument the functions when key is found at the start of the function name
if(std::regex_search(function_name, leading))
{
return _report("Excluding", "recommended-leading-match", 3);
}
// don't instrument the functions when key is found at the end of the function name
if(std::regex_search(function_name, trailing))
{
return _report("Excluding", "recommended-trailing-match", 3);
}
return false;
}
@@ -364,6 +489,8 @@ module_function::is_loop_address_range_constrained() const
bool
module_function::is_num_instructions_constrained() const
{
if(!loop_blocks.empty()) return is_loop_num_instructions_constrained();
if(num_instructions < min_instructions)
{
messages.emplace_back(2, "Skipping", "function", "min-instructions");
@@ -373,6 +500,20 @@ module_function::is_num_instructions_constrained() const
return false;
}
bool
module_function::is_loop_num_instructions_constrained() const
{
if(loop_blocks.empty()) return false;
if(num_instructions < min_loop_instructions)
{
messages.emplace_back(2, "Skipping", "function", "min-instructions-loop");
return true;
}
return false;
}
bool
module_function::can_instrument_entry() const
{
@@ -459,10 +600,8 @@ module_function::operator()(address_space_t* _addr_space, procedure_t* _entr_tra
auto _entr = _trace_entr.get(_entr_trace);
auto _exit = _trace_exit.get(_exit_trace);
if(insert_instr(_addr_space, function, _entr, BPatch_entry, nullptr, nullptr,
instr_traps) &&
insert_instr(_addr_space, function, _exit, BPatch_exit, nullptr, nullptr,
instr_traps))
if(insert_instr(_addr_space, function, _entr, BPatch_entry) &&
insert_instr(_addr_space, function, _exit, BPatch_exit))
{
messages.emplace_back(1, "Instrumenting", "function", "no-constraint");
++_count.first;
@@ -516,3 +655,92 @@ module_function::operator()(address_space_t* _addr_space, procedure_t* _entr_tra
return _count;
}
void
module_function::register_source(address_space_t* _addr_space, procedure_t* _entr_trace,
const std::vector<point_t*>& _entr_points) const
{
switch(coverage_mode)
{
case CODECOV_FUNCTION:
{
auto _name = signature.get_coverage(false);
auto _trace_entr =
omnitrace_call_expr(signature.m_file, signature.m_name,
signature.m_row.first, start_address, _name);
auto _entr = _trace_entr.get(_entr_trace);
if(insert_instr(_addr_space, _entr_points, _entr, BPatch_entry))
{
messages.emplace_back(1, "Code Coverage", "function", "no-constraint");
}
break;
}
case CODECOV_BASIC_BLOCK:
{
for(auto&& itr : get_basic_block_file_line_info(module, function))
{
auto _start_addr = itr.second.start_address;
auto& _signature = itr.second.signature;
auto _name = _signature.get_coverage(true);
auto _trace_entr =
omnitrace_call_expr(_signature.m_file, _signature.m_name,
_signature.m_row.first, _start_addr, _name);
auto _entr = _trace_entr.get(_entr_trace);
if(insert_instr(_addr_space, _entr_points, _entr, BPatch_entry))
{
messages.emplace_back(1, "Code Coverage", "basic_block",
"no-constraint");
}
}
break;
}
case CODECOV_NONE: break;
}
}
std::pair<size_t, size_t>
module_function::register_coverage(address_space_t* _addr_space,
procedure_t* _entr_trace) const
{
std::pair<size_t, size_t> _count = { 0, 0 };
switch(coverage_mode)
{
case CODECOV_FUNCTION:
{
auto _trace_entr =
omnitrace_call_expr(signature.m_file, signature.m_name, start_address);
auto _entr = _trace_entr.get(_entr_trace);
if(insert_instr(_addr_space, function, _entr, BPatch_entry))
{
messages.emplace_back(1, "Code Coverage", "function", "no-constraint");
++_count.first;
}
break;
}
case CODECOV_BASIC_BLOCK:
{
for(auto&& itr : get_basic_block_file_line_info(module, function))
{
auto _start_addr = itr.second.start_address;
auto& _signature = itr.second.signature;
auto _trace_entr = omnitrace_call_expr(_signature.m_file,
_signature.m_name, _start_addr);
auto _entr = _trace_entr.get(_entr_trace);
if(insert_instr(_addr_space, _entr, BPatch_entry, itr.first))
{
++_count.second;
messages.emplace_back(1, "Code Coverage", "basic_block",
"no-constraint");
}
}
verbprintf(0, "Basic-block code coverage is not available yet\n");
break;
}
case CODECOV_NONE: break;
}
return _count;
}
@@ -46,12 +46,20 @@ struct module_function
module_function(module_t* mod, procedure_t* proc);
// code coverage
void register_source(address_space_t* _addr_space, procedure_t* _entr_trace,
const std::vector<point_t*>&) const;
std::pair<size_t, size_t> register_coverage(address_space_t* _addr_space,
procedure_t* _entr_trace) const;
// instrumentation
std::pair<size_t, size_t> operator()(address_space_t* _addr_space,
procedure_t* _entr_trace,
procedure_t* _exit_trace) const;
// applies logic for all "is_*" and "can_*" checks below
bool should_instrument() const;
bool should_coverage_instrument() const;
// hard constraints
bool is_instrumentable() const; // checks whether can instrument
@@ -98,8 +106,10 @@ struct module_function
bool is_overlapping() const; // checks if func overlaps
private:
bool is_loop_num_instructions_constrained() const; // checks loop instr constraint
bool is_loop_address_range_constrained() const; // checks loop addr range constraint
bool contains_dynamic_callsites() const;
bool should_instrument(bool _coverage) const;
public:
template <typename ArchiveT>
@@ -176,6 +186,7 @@ module_function::serialize(ArchiveT& ar, const unsigned)
ar.setNextName("heuristics");
ar.startNode();
ar(cereal::make_nvp("should_instrument", should_instrument()),
cereal::make_nvp("should_coverage_instrument", should_coverage_instrument()),
cereal::make_nvp("is_instrumentable", is_instrumentable()),
cereal::make_nvp("can_instrument_entry", can_instrument_entry()),
cereal::make_nvp("can_instrument_exit", can_instrument_exit()),
@@ -23,12 +23,17 @@
#include "omnitrace.hpp"
#include "fwd.hpp"
#include <timemory/config.hpp>
#include <timemory/settings.hpp>
#include <algorithm>
#include <chrono>
#include <csignal>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <regex>
#include <stdexcept>
#include <string>
#include <sys/stat.h>
@@ -62,50 +67,53 @@ string_t prefer_library = {};
//
// global variables
//
patch_pointer_t bpatch = {};
call_expr_t* terminate_expr = nullptr;
snippet_vec_t init_names = {};
snippet_vec_t fini_names = {};
fmodset_t available_module_functions = {};
fmodset_t instrumented_module_functions = {};
fmodset_t overlapping_module_functions = {};
fmodset_t excluded_module_functions = {};
fixed_modset_t fixed_module_functions = {};
regexvec_t func_include = {};
regexvec_t func_exclude = {};
regexvec_t file_include = {};
regexvec_t file_exclude = {};
regexvec_t file_restrict = {};
regexvec_t func_restrict = {};
patch_pointer_t bpatch = {};
call_expr_t* terminate_expr = nullptr;
snippet_vec_t init_names = {};
snippet_vec_t fini_names = {};
fmodset_t available_module_functions = {};
fmodset_t instrumented_module_functions = {};
fmodset_t coverage_module_functions = {};
fmodset_t overlapping_module_functions = {};
fmodset_t excluded_module_functions = {};
fixed_modset_t fixed_module_functions = {};
regexvec_t func_include = {};
regexvec_t func_exclude = {};
regexvec_t file_include = {};
regexvec_t file_exclude = {};
regexvec_t file_restrict = {};
regexvec_t func_restrict = {};
CodeCoverageMode coverage_mode = CODECOV_NONE;
namespace
{
bool binary_rewrite = false;
bool is_attached = false;
bool use_mpi = false;
bool is_static_exe = false;
bool is_driver = false;
bool explicit_dump_and_exit = false;
size_t batch_size = 50;
strset_t extra_libs = {};
std::vector<std::pair<uint64_t, string_t>> hash_ids = {};
std::map<string_t, bool> use_stubs = {};
std::map<string_t, procedure_t*> beg_stubs = {};
std::map<string_t, procedure_t*> end_stubs = {};
strvec_t init_stub_names = {};
strvec_t fini_stub_names = {};
strset_t used_stub_names = {};
std::vector<call_expr_pointer_t> env_variables = {};
std::map<string_t, call_expr_pointer_t> beg_expr = {};
std::map<string_t, call_expr_pointer_t> end_expr = {};
const auto npos_v = string_t::npos;
string_t instr_mode = "trace";
string_t print_instrumented = {};
string_t print_excluded = {};
string_t print_available = {};
string_t print_overlapping = {};
strset_t print_formats = { "txt", "json" };
std::string modfunc_dump_dir = {};
bool binary_rewrite = false;
bool is_attached = false;
bool use_mpi = false;
bool is_static_exe = false;
bool simulate = false;
size_t batch_size = 50;
strset_t extra_libs = {};
std::vector<std::pair<uint64_t, string_t>> hash_ids = {};
std::map<string_t, bool> use_stubs = {};
std::map<string_t, procedure_t*> beg_stubs = {};
std::map<string_t, procedure_t*> end_stubs = {};
strvec_t init_stub_names = {};
strvec_t fini_stub_names = {};
strset_t used_stub_names = {};
strvec_t env_config_variables = {};
std::vector<call_expr_pointer_t> env_variables = {};
std::map<string_t, call_expr_pointer_t> beg_expr = {};
std::map<string_t, call_expr_pointer_t> end_expr = {};
const auto npos_v = string_t::npos;
string_t instr_mode = "trace";
string_t print_coverage = {};
string_t print_instrumented = {};
string_t print_excluded = {};
string_t print_available = {};
string_t print_overlapping = {};
strset_t print_formats = { "txt", "json" };
std::string modfunc_dump_dir = {};
auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize;
std::string
@@ -176,6 +184,7 @@ main(int argc, char** argv)
fixed_module_functions = {
{ &available_module_functions, false },
{ &instrumented_module_functions, false },
{ &coverage_module_functions, false },
{ &excluded_module_functions, false },
{ &overlapping_module_functions, false },
};
@@ -218,7 +227,9 @@ main(int argc, char** argv)
int k = 0;
for(int j = i + 1; j < argc; ++j, ++k)
{
copy_str(_cmdv[k], argv[j]);
auto _v =
std::regex_replace(argv[j], std::regex{ "(.*)([ \t\n\r]+)$" }, "$1");
copy_str(_cmdv[k], _v.c_str());
}
mutname = _cmdv[0];
break;
@@ -256,9 +267,9 @@ main(int argc, char** argv)
<< std::endl;
}
if(_cmdc > 0)
std::cout << "\n[omnitrace][exe][command]: " << cmd_string(_cmdc, _cmdv)
<< "\n\n";
verbprintf(0, "\n");
verbprintf(0, "command :: '%s'...\n", cmd_string(_cmdc, _cmdv).c_str());
verbprintf(0, "\n");
if(_cmdc > 0) cmdv0 = _cmdv[0];
@@ -299,7 +310,7 @@ main(int argc, char** argv)
"function lists, e.g. available-instr.txt")
.max_count(1)
.dtype("bool")
.action([](parser_t& p) { explicit_dump_and_exit = p.get<bool>("simulate"); });
.action([](parser_t& p) { simulate = p.get<bool>("simulate"); });
parser
.add_argument({ "--print-format" },
"Output format for diagnostic "
@@ -317,12 +328,14 @@ main(int argc, char** argv)
"function lists, e.g. {print-dir}/available-instr.txt")
.count(1)
.dtype("string")
.action([](parser_t& p) { modfunc_dump_dir = p.get<std::string>("print-dir"); });
.action([](parser_t& p) {
tim::settings::output_path() = p.get<std::string>("print-dir");
});
parser
.add_argument(
{ "--print-available" },
"Print the available entities for instrumentation (functions, modules, or "
"module-function pair) to stdout applying regular expressions and exit")
"module-function pair) to stdout after applying regular expressions")
.count(1)
.choices({ "functions", "modules", "functions+", "pair", "pair+" })
.action(
@@ -331,18 +344,27 @@ main(int argc, char** argv)
.add_argument(
{ "--print-instrumented" },
"Print the instrumented entities (functions, modules, or module-function "
"pair) to stdout after applying regular expressions and exit")
"pair) to stdout after applying regular expressions")
.count(1)
.choices({ "functions", "modules", "functions+", "pair", "pair+" })
.action([](parser_t& p) {
print_instrumented = p.get<std::string>("print-instrumented");
});
parser
.add_argument({ "--print-coverage" },
"Print the instrumented coverage entities (functions, modules, or "
"module-function "
"pair) to stdout after applying regular expressions")
.count(1)
.choices({ "functions", "modules", "functions+", "pair", "pair+" })
.action(
[](parser_t& p) { print_coverage = p.get<std::string>("print-coverage"); });
parser
.add_argument({ "--print-excluded" },
"Print the entities for instrumentation (functions, modules, or "
"module-function "
"pair) which are excluded from the instrumentation to stdout after "
"applying regular expressions and exit")
"applying regular expressions")
.count(1)
.choices({ "functions", "modules", "functions+", "pair", "pair+" })
.action(
@@ -352,7 +374,7 @@ main(int argc, char** argv)
{ "--print-overlapping" },
"Print the entities for instrumentation (functions, modules, or "
"module-function pair) which overlap other function calls or have multiple "
"entry points to stdout applying regular expressions and exit")
"entry points to stdout after applying regular expressions")
.count(1)
.choices({ "functions", "modules", "functions+", "pair", "pair+" })
.action([](parser_t& p) {
@@ -391,9 +413,13 @@ main(int argc, char** argv)
"Instrumentation mode. 'trace' mode instruments the selected "
"functions, 'sampling' mode only instruments the main function to "
"start and stop the sampler.")
.choices({ "trace", "sampling" })
.choices({ "trace", "sampling", "coverage" })
.count(1)
.action([](parser_t& p) { instr_mode = p.get<string_t>("mode"); });
.action([](parser_t& p) {
instr_mode = p.get<string_t>("mode");
if(instr_mode == "coverage" && !p.exists("coverage"))
coverage_mode = CODECOV_FUNCTION;
});
if(_cmdc == 0)
{
parser
@@ -433,23 +459,6 @@ main(int argc, char** argv)
"The primary function to instrument around, e.g. 'main'")
.count(1)
.action([](parser_t& p) { main_fname = p.get<string_t>("main-function"); });
/*
parser
.add_argument({ "-s", "--stubs" }, "Instrument with library stubs for LD_PRELOAD")
.dtype("boolean")
.max_count(1)
.action([&inputlib](parser_t& p) {
if(p.get<bool>("stubs"))
{
for(auto& itr : inputlib)
itr += "-stubs";
}
});
*/
parser.add_argument({ "--driver" }, "Force main or _init/_fini instrumentation")
.dtype("boolean")
.max_count(1)
.action([](parser_t& p) { is_driver = p.get<bool>("driver"); });
parser
.add_argument({ "--load" },
"Supplemental instrumentation library names w/o extension (e.g. "
@@ -471,6 +480,7 @@ main(int argc, char** argv)
std::map<std::string, fmodset_t*> module_function_map = {
{ "available_module_functions", &available_module_functions },
{ "instrumented_module_functions", &instrumented_module_functions },
{ "coverage_module_functions", &coverage_module_functions },
{ "excluded_module_functions", &excluded_module_functions },
{ "overlapping_module_functions", &overlapping_module_functions },
};
@@ -545,6 +555,12 @@ main(int argc, char** argv)
use_line_info = true;
}
});
parser.add_argument()
.names({ "-C", "--config" })
.dtype("string")
.min_count(1)
.description("Read in a configuration file and encode these values as the "
"defaults in the executable");
parser.add_argument()
.names({ "-d", "--default-components" })
.dtype("string")
@@ -626,6 +642,18 @@ main(int argc, char** argv)
.action([](parser_t& p) {
min_loop_address_range = p.get<size_t>("min-address-range-loop");
});
parser.add_argument({ "--coverage" }, "Enable recording the code coverage")
.max_count(1)
.choices({ "none", "function", "basic_block" })
.action([](parser_t& p) {
auto _v = p.get<std::string>("coverage");
if(_v == "function" || _v.empty())
coverage_mode = CODECOV_FUNCTION;
else if(_v == "basic_block")
coverage_mode = CODECOV_BASIC_BLOCK;
else
coverage_mode = CODECOV_NONE;
});
parser
.add_argument({ "--dynamic-callsites" },
"Force instrumentation if a function has dynamic callsites (e.g. "
@@ -703,6 +731,41 @@ main(int argc, char** argv)
return -1;
}
if(parser.exists("config"))
{
struct omnitrace_env_config_s
{};
auto _configs = parser.get<strvec_t>("config");
for(auto&& itr : _configs)
{
auto _settings = tim::settings::push<omnitrace_env_config_s>();
for(auto&& itr : *_settings)
{
itr.second->set_config_updated(false);
itr.second->set_environ_updated(false);
}
_settings->read(itr);
for(auto&& itr : *_settings)
{
if(itr.second && itr.second->get_config_updated())
{
env_config_variables.emplace_back(TIMEMORY_JOIN(
'=', itr.second->get_env_name(), itr.second->as_string()));
verbprintf(1, "Exporting known config value :: %s\n",
env_config_variables.back().c_str());
}
}
for(auto&& itr : _settings->get_unknown_configs())
{
env_config_variables.emplace_back(
TIMEMORY_JOIN('=', itr.first, itr.second));
verbprintf(1, "Exporting unknown config value :: %s\n",
env_config_variables.back().c_str());
}
tim::settings::pop<omnitrace_env_config_s>();
}
}
auto _handle_heuristics = [&parser](std::string&& _exists, std::string&& _not_exists,
auto& _field, auto _value, std::string&& _msg,
bool _cond) {
@@ -781,20 +844,17 @@ main(int argc, char** argv)
outfile.c_str());
}
if(modfunc_dump_dir.empty())
if(binary_rewrite)
{
modfunc_dump_dir = tim::get_env<std::string>("OMNITRACE_OUTPUT_PATH", "");
if(modfunc_dump_dir.empty())
{
auto _exe_base = (binary_rewrite) ? outfile : std::string{ cmdv0 };
auto _pos = _exe_base.find_last_of('/');
if(_pos != std::string::npos && _pos + 1 < _exe_base.length())
_exe_base = _exe_base.substr(_pos + 1);
modfunc_dump_dir = TIMEMORY_JOIN("-", "omnitrace", _exe_base, "output");
}
auto* _save = _cmdv[0];
_cmdv[0] = const_cast<char*>(outfile.c_str());
tim::timemory_init(_cmdc, _cmdv, "omnitrace-");
_cmdv[0] = _save;
}
else
{
tim::timemory_init(_cmdc, _cmdv, "omnitrace-");
}
if(verbose_level >= 0) tim::makedir(modfunc_dump_dir);
//----------------------------------------------------------------------------------//
//
@@ -914,9 +974,7 @@ main(int argc, char** argv)
// for runtime instrumentation, we need to set this before the process gets created
if(!binary_rewrite)
{
#if defined(OMNITRACE_USE_ROCTRACER)
tim::set_env("HSA_ENABLE_INTERRUPT", "0", 0);
#endif
if(_pid >= 0)
{
verbprintf(-10, "#-------------------------------------------------------"
@@ -1053,15 +1111,10 @@ main(int argc, char** argv)
std::cout << '\n' << std::endl;
}
auto _output_prefix = tim::get_env<std::string>("OMNITRACE_OUTPUT_PREFIX", "");
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "available-instr")),
available_module_functions, 1, werror, "available-instr", print_formats);
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "overlapping-instr")),
overlapping_module_functions, 1, werror, "overlapping_module_functions",
dump_info("available-instr", available_module_functions, 1, werror, "available-instr",
print_formats);
dump_info("overlapping-instr", overlapping_module_functions, 1, werror,
"overlapping_module_functions", print_formats);
//----------------------------------------------------------------------------------//
//
@@ -1139,8 +1192,8 @@ main(int argc, char** argv)
//
//----------------------------------------------------------------------------------//
auto* _mutatee_init = find_function(app_image, "_init");
auto* _mutatee_fini = find_function(app_image, "_fini");
auto* main_init = find_function(app_image, "_init");
auto* main_fini = find_function(app_image, "_fini");
auto* main_func = find_function(app_image, main_fname.c_str());
auto* mpi_init_func = find_function(app_image, "MPI_Init", { "MPI_Init_thread" });
auto* mpi_fini_func = find_function(app_image, "MPI_Finalize");
@@ -1168,12 +1221,14 @@ main(int argc, char** argv)
verbprintf(0, "Finding instrumentation functions...\n");
auto* entr_trace = find_function(app_image, "omnitrace_push_trace");
auto* exit_trace = find_function(app_image, "omnitrace_pop_trace");
auto* init_func = find_function(app_image, "omnitrace_init");
auto* fini_func = find_function(app_image, "omnitrace_finalize");
auto* env_func = find_function(app_image, "omnitrace_set_env");
auto* mpi_func = find_function(app_image, "omnitrace_set_mpi");
auto* init_func = find_function(app_image, "omnitrace_init");
auto* fini_func = find_function(app_image, "omnitrace_finalize");
auto* env_func = find_function(app_image, "omnitrace_set_env");
auto* mpi_func = find_function(app_image, "omnitrace_set_mpi");
auto* entr_trace = find_function(app_image, "omnitrace_push_trace");
auto* exit_trace = find_function(app_image, "omnitrace_pop_trace");
auto* reg_src_func = find_function(app_image, "omnitrace_register_source");
auto* reg_cov_func = find_function(app_image, "omnitrace_register_coverage");
if(!main_func && main_fname == "main") main_func = find_function(app_image, "_main");
@@ -1293,36 +1348,42 @@ main(int argc, char** argv)
//
//----------------------------------------------------------------------------------//
if(!main_func && is_driver)
if(!main_func)
{
errprintf(0, "could not find '%s'\n", main_fname.c_str());
if(!_mutatee_init || !_mutatee_fini)
if(!main_init && !main_fini)
{
errprintf(-1, "could not find '%s' or '%s', aborting\n", "_init", "_fini");
errprintf(-1, "could not find '%s', '_init' or '_fini', aborting...\n",
main_fname.c_str());
}
else if(!main_init)
{
errprintf(-1, "could not find '%s' or '_init', aborting...\n",
main_fname.c_str());
}
else if(!main_fini)
{
errprintf(-1, "could not find '%s' or '_fini', aborting...\n",
main_fname.c_str());
}
else
{
errprintf(0, "using '%s' and '%s' in lieu of '%s'...", "_init", "_fini",
main_fname.c_str());
verbprintf(0, "using '%s' and '%s' in lieu of '%s'...", "_init", "_fini",
main_fname.c_str());
}
}
else if(!main_func && !is_driver)
{
verbprintf(0, "Warning! No main function and is not driver!\n");
}
using pair_t = std::pair<procedure_t*, string_t>;
for(const auto& itr :
{ pair_t(main_func, main_fname), pair_t(entr_trace, "omnitrace_push_trace"),
{ pair_t(entr_trace, "omnitrace_push_trace"),
pair_t(exit_trace, "omnitrace_pop_trace"), pair_t(init_func, "omnitrace_init"),
pair_t(fini_func, "omnitrace_finalize"),
pair_t(env_func, "omnitrace_set_env") })
pair_t(fini_func, "omnitrace_finalize"), pair_t(env_func, "omnitrace_set_env"),
pair_t(reg_src_func, "omnitrace_register_source"),
pair_t(reg_cov_func, "omnitrace_register_coverage") })
{
if(itr.first == main_func && !is_driver) continue;
if(!itr.first)
{
errprintf(-1, "could not find required function :: '%s;\n",
errprintf(-1, "could not find required function :: '%s'\n",
itr.second.c_str());
}
}
@@ -1350,8 +1411,8 @@ main(int argc, char** argv)
bool has_debug_info = false;
check_for_debug_info(has_debug_info, main_func);
check_for_debug_info(has_debug_info, _mutatee_init);
check_for_debug_info(has_debug_info, _mutatee_fini);
check_for_debug_info(has_debug_info, main_init);
check_for_debug_info(has_debug_info, main_fini);
//----------------------------------------------------------------------------------//
//
@@ -1370,18 +1431,15 @@ main(int argc, char** argv)
main_exit_points = main_func->findPoint(BPatch_exit);
verbprintf(2, "Done\n");
}
else if(is_driver)
else
{
if(_mutatee_init)
{
verbprintf(2, "Finding init entry...\n");
main_entr_points = _mutatee_init->findPoint(BPatch_entry);
}
if(_mutatee_fini)
{
verbprintf(2, "Finding fini exit...\n");
main_exit_points = _mutatee_fini->findPoint(BPatch_exit);
}
verbprintf(2, "Finding init entry... ");
main_entr_points = main_init->findPoint(BPatch_entry);
verbprintf(2, "Done\n");
verbprintf(2, "Finding fini exit... ");
main_exit_points = main_fini->findPoint(BPatch_exit);
verbprintf(2, "Done\n");
}
//----------------------------------------------------------------------------------//
@@ -1452,9 +1510,12 @@ main(int argc, char** argv)
// prioritize the user environment arguments
auto env_vars = parser.get<strvec_t>("env");
env_vars.reserve(env_vars.size() + env_config_variables.size());
for(auto&& itr : env_config_variables)
env_vars.emplace_back(itr);
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MODE", instr_mode));
#if defined(OMNITRACE_USE_ROCTRACER)
env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_ENABLE_INTERRUPT", "0"));
#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0
env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_TOOLS_LIB", _libname));
#endif
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MPI_INIT", "OFF"));
@@ -1467,6 +1528,8 @@ main(int argc, char** argv)
env_vars.emplace_back(
TIMEMORY_JOIN('=', "OMNITRACE_USE_MPIP",
(binary_rewrite && use_mpi && use_mpip) ? "ON" : "OFF"));
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_USE_CODE_COVERAGE",
(coverage_mode != CODECOV_NONE) ? "ON" : "OFF"));
if(use_mpi) env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_USE_PID", "ON"));
for(auto& itr : env_vars)
@@ -1508,7 +1571,7 @@ main(int argc, char** argv)
if(umpi_call) init_names.emplace_back(umpi_call.get());
if(init_call) init_names.emplace_back(init_call.get());
if(main_beg_call) init_names.emplace_back(main_beg_call.get());
if(main_func && main_beg_call) init_names.emplace_back(main_beg_call.get());
for(const auto& itr : end_expr)
if(itr.second) fini_names.emplace_back(itr.second.get());
@@ -1524,14 +1587,20 @@ main(int argc, char** argv)
{
for(const auto& itr : available_module_functions)
{
bool _is_not_main = itr.function != main_func && itr.function != main_init &&
itr.function != main_fini;
if(itr.should_instrument())
{
if(itr.function != main_func && itr.function != _mutatee_init &&
itr.function != _mutatee_fini)
if(_is_not_main)
_insert_module_function(instrumented_module_functions, itr);
}
else
_insert_module_function(excluded_module_functions, itr);
if(coverage_mode != CODECOV_NONE)
{
if(itr.should_coverage_instrument() && _is_not_main)
_insert_module_function(coverage_module_functions, itr);
}
if(itr.is_overlapping())
_insert_module_function(overlapping_module_functions, itr);
}
@@ -1581,55 +1650,103 @@ main(int argc, char** argv)
verbprintf(2, "Beginning instrumentation loop...\n");
verbprintf(1, "\n");
std::map<std::string, std::pair<size_t, size_t>> _pass_info{};
const int _pass_verbose_lvl = 2;
for(const auto& itr : instrumented_module_functions)
{
auto _count = itr(addr_space, entr_trace, exit_trace);
_pass_info[itr.module_name].first += _count.first;
_pass_info[itr.module_name].second += _count.second;
auto _report = [](int _lvl, const string_t& _action, const string_t& _type,
const string_t& _reason, const string_t& _name,
const std::string& _extra = {}) {
static std::map<std::string, strset_t> already_reported{};
auto _key = _type + _action + _reason;
if(already_reported[_key].count(_name) == 0)
{
verbprintf(_lvl, "[%s][%s] %s :: '%s'", _type.c_str(), _action.c_str(),
_reason.c_str(), _name.c_str());
if(!_extra.empty()) verbprintf_bare(_lvl, " (%s)", _extra.c_str());
verbprintf_bare(_lvl, "...\n");
already_reported[_key].insert(_name);
}
};
for(const auto& mitr : itr.messages)
_report(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr),
std::get<3>(mitr),
std::get<2>(mitr) == "module" ? itr.module_name : itr.function_name);
}
verbprintf(1, "\n");
// report the instrumented
for(auto& itr : _pass_info)
{
auto _valid = (verbose_level > _pass_verbose_lvl ||
(itr.second.first + itr.second.second) > 0);
if(_valid)
auto _report_info = [](int _lvl, const string_t& _action, const string_t& _type,
const string_t& _reason, const string_t& _name,
const std::string& _extra = {}) {
static std::map<std::string, strset_t> already_reported{};
auto _key = TIMEMORY_JOIN('_', _type, _action, _reason, _name, _extra);
if(already_reported[_key].count(_name) == 0)
{
verbprintf(_lvl, "[%s][%s] %s :: '%s'", _type.c_str(), _action.c_str(),
_reason.c_str(), _name.c_str());
if(!_extra.empty()) verbprintf_bare(_lvl, " (%s)", _extra.c_str());
verbprintf_bare(_lvl, "...\n");
already_reported[_key].insert(_name);
}
};
if(instr_mode != "coverage")
{
std::map<std::string, std::pair<size_t, size_t>> _pass_info{};
const int _pass_verbose_lvl = 1;
for(const auto& itr : instrumented_module_functions)
{
auto _count = itr(addr_space, entr_trace, exit_trace);
_pass_info[itr.module_name].first += _count.first;
_pass_info[itr.module_name].second += _count.second;
for(const auto& mitr : itr.messages)
_report_info(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr),
std::get<3>(mitr),
std::get<2>(mitr) == "module" ? itr.module_name
: itr.function_name);
}
// report the trace instrumented functions
for(auto& itr : _pass_info)
{
auto _valid = (verbose_level > _pass_verbose_lvl ||
(itr.second.first + itr.second.second) > 0);
if(!_valid) continue;
verbprintf(_pass_verbose_lvl, "%4zu instrumented procedures in %s\n",
itr.second.first, itr.first.c_str());
_valid = (loop_level_instr &&
(verbose_level > _pass_verbose_lvl || itr.second.second > 0));
if(_valid)
{
verbprintf(_pass_verbose_lvl, "%4zu instrumented loop procedures in %s\n",
verbprintf(_pass_verbose_lvl, "%4zu instrumented loops in procedure %s\n",
itr.second.second, itr.first.c_str());
}
}
}
if(coverage_mode != CODECOV_NONE)
{
std::map<std::string, std::pair<size_t, size_t>> _covr_info{};
const int _covr_verbose_lvl = 1;
for(const auto& itr : coverage_module_functions)
{
itr.register_source(addr_space, reg_src_func, *main_entr_points);
auto _count = itr.register_coverage(addr_space, reg_cov_func);
_covr_info[itr.module_name].first += _count.first;
_covr_info[itr.module_name].second += _count.second;
for(const auto& mitr : itr.messages)
_report_info(std::get<0>(mitr), std::get<1>(mitr), std::get<2>(mitr),
std::get<3>(mitr),
std::get<2>(mitr) == "module" ? itr.module_name
: itr.function_name);
}
// report the coverage instrumented functions
for(auto& itr : _covr_info)
{
auto _valid = (verbose_level > _covr_verbose_lvl ||
(itr.second.first + itr.second.second) > 0);
if(!_valid) continue;
switch(coverage_mode)
{
case CODECOV_NONE:
{
break;
}
case CODECOV_FUNCTION:
{
verbprintf(_covr_verbose_lvl, "%4zu coverage functions in %s\n",
itr.second.first, itr.first.c_str());
break;
}
case CODECOV_BASIC_BLOCK:
{
verbprintf(_covr_verbose_lvl, "%4zu coverage basic blocks in %s\n",
itr.second.second, itr.first.c_str());
break;
}
}
}
}
verbprintf(1, "\n");
if(app_thread)
{
verbprintf(1, "Finalizing insertion set...\n");
@@ -1692,26 +1809,17 @@ main(int argc, char** argv)
_insert_module_function(excluded_module_functions, itr);
}
bool _dump_and_exit = ((print_available.length() + print_instrumented.length() +
print_overlapping.length() + print_excluded.length()) > 0) ||
explicit_dump_and_exit;
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "available-instr")),
available_module_functions, 0, werror, "available_module_functions",
print_formats);
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "instrumented-instr")),
instrumented_module_functions, 0, werror, "instrumented_module_functions",
print_formats);
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "excluded-instr")),
excluded_module_functions, 0, werror, "excluded_module_functions",
print_formats);
dump_info(TIMEMORY_JOIN('/', modfunc_dump_dir,
TIMEMORY_JOIN("", _output_prefix, "overlapping-instr")),
overlapping_module_functions, 0, werror, "overlapping_module_functions",
print_formats);
dump_info("available-instr", available_module_functions, 0, werror,
"available_module_functions", print_formats);
dump_info("instrumented-instr", instrumented_module_functions, 0, werror,
"instrumented_module_functions", print_formats);
dump_info("excluded-instr", excluded_module_functions, 0, werror,
"excluded_module_functions", print_formats);
if(coverage_mode != CODECOV_NONE)
dump_info("coverage-instr", coverage_module_functions, 0, werror,
"coverage_module_functions", print_formats);
dump_info("overlapping-instr", overlapping_module_functions, 0, werror,
"overlapping_module_functions", print_formats);
auto _dump_info = [](const std::string& _label, const string_t& _mode,
const fmodset_t& _modset) {
@@ -1733,13 +1841,13 @@ main(int argc, char** argv)
{
for(const auto& itr : _modset)
_insert(itr.module_name, TIMEMORY_JOIN("", "[", itr.function_name, "][",
itr.address_range, "]"));
itr.num_instructions, "]"));
}
else if(_mode == "functions+")
{
for(const auto& itr : _modset)
_insert(itr.module_name, TIMEMORY_JOIN("", "[", itr.signature.get(), "][",
itr.address_range, "]"));
itr.num_instructions, "]"));
}
else if(_mode == "pair")
{
@@ -1748,7 +1856,7 @@ main(int argc, char** argv)
std::stringstream _ss{};
_ss << std::boolalpha;
_ss << "" << itr.module_name << "] --> [" << itr.function_name << "]["
<< itr.address_range << "]";
<< itr.num_instructions << "]";
_insert(itr.module_name, _ss.str());
}
}
@@ -1759,7 +1867,7 @@ main(int argc, char** argv)
std::stringstream _ss{};
_ss << std::boolalpha;
_ss << "[" << itr.module_name << "] --> [" << itr.signature.get() << "]["
<< itr.address_range << "]";
<< itr.num_instructions << "]";
_insert(itr.module_name, _ss.str());
}
}
@@ -1785,10 +1893,12 @@ main(int argc, char** argv)
_dump_info("instrumented", print_instrumented, instrumented_module_functions);
if(!print_excluded.empty())
_dump_info("excluded", print_excluded, excluded_module_functions);
if(!print_coverage.empty())
_dump_info("coverage", print_coverage, coverage_module_functions);
if(!print_overlapping.empty())
_dump_info("overlapping", print_overlapping, overlapping_module_functions);
if(_dump_and_exit) exit(EXIT_SUCCESS);
if(simulate) exit(EXIT_SUCCESS);
//----------------------------------------------------------------------------------//
//
@@ -1922,154 +2032,6 @@ main(int argc, char** argv)
return code;
}
//======================================================================================//
bool
instrument_module(const string_t& file_name)
{
auto _report = [&file_name](const string_t& _action, const string_t& _reason,
int _lvl) {
static strset_t already_reported{};
if(already_reported.count(file_name) == 0)
{
verbprintf(_lvl, "%s module [%s] : '%s'...\n", _action.c_str(),
_reason.c_str(), file_name.c_str());
already_reported.insert(file_name);
}
};
static std::regex ext_regex{ "\\.(s|S)$", regex_opts };
static std::regex sys_regex{ "^(s|k|e|w)_[A-Za-z_0-9\\-]+\\.(c|C)$", regex_opts };
static std::regex sys_build_regex{ "^(\\.\\./sysdeps/|/build/)", regex_opts };
static std::regex dyninst_regex{ "(dyninst|DYNINST|(^|/)RT[[:graph:]]+\\.c$)",
regex_opts };
static std::regex dependlib_regex{ "^(lib|)(omnitrace|pthread|caliper|gotcha|papi|"
"cupti|TAU|likwid|pfm|nvperf|unwind)",
regex_opts };
static std::regex core_cmod_regex{
"^(malloc|(f|)lock|sig|sem)[a-z_]+(|64|_r|_l)\\.c$"
};
static std::regex core_lib_regex{
"^(lib|)(c|dl|dw|pthread|tcmalloc|profiler|"
"tbbmalloc|tbbmalloc_proxy|malloc|stdc\\+\\+)(-|\\.)",
regex_opts
};
static std::regex prefix_regex{ "^(_|\\.[a-zA-Z0-9])", regex_opts };
// file extensions that should not be instrumented
if(std::regex_search(file_name, ext_regex))
{
return (_report("Excluding", "file extension", 3), false);
}
// system modules that should not be instrumented (wastes time)
if(std::regex_search(file_name, sys_regex) ||
std::regex_search(file_name, sys_build_regex))
{
return (_report("Excluding", "system module", 3), false);
}
// dyninst modules that must not be instrumented
if(std::regex_search(file_name, dyninst_regex))
{
return (_report("Excluding", "dyninst module", 3), false);
}
// modules used by omnitrace and dependent libraries
if(std::regex_search(file_name, core_lib_regex) ||
std::regex_search(file_name, core_cmod_regex))
{
return (_report("Excluding", "core module", 3), false);
}
// modules used by omnitrace and dependent libraries
if(std::regex_search(file_name, dependlib_regex))
{
return (_report("Excluding", "dependency module", 3), false);
}
// known set of modules whose starting sequence of characters suggest it should not be
// instrumented (wastes time)
if(std::regex_search(file_name, prefix_regex))
{
return (_report("Excluding", "prefix match", 3), false);
}
_report("Including", "no constraint", 2);
return true;
}
//======================================================================================//
bool
instrument_entity(const string_t& function_name)
{
auto _report = [&function_name](const string_t& _action, const string_t& _reason,
int _lvl) {
static strset_t already_reported{};
if(already_reported.count(function_name) == 0)
{
verbprintf(_lvl, "%s function [%s] : '%s'...\n", _action.c_str(),
_reason.c_str(), function_name.c_str());
already_reported.insert(function_name);
}
};
static std::regex exclude(
"(omnitrace|tim::|N3tim|MPI_Init|MPI_Finalize|dyninst|tm_clones)", regex_opts);
static std::regex exclude_cxx(
"(std::_Sp_counted_base|std::(use|has)_facet|std::locale|::sentry|^std::_|::_(M|"
"S)_|::basic_string[a-zA-Z,<>: ]+::_M_create|::__|::_(Alloc|State)|"
"std::(basic_|)(ifstream|ios|istream|ostream|stream))",
regex_opts);
static std::regex leading("^(_|\\.|frame_dummy|transaction clone|virtual "
"thunk|non-virtual thunk|\\(|targ|kmp_threadprivate_)",
regex_opts);
static std::regex trailing(
"(_|\\.part\\.[0-9]+|\\.constprop\\.[0-9]+|\\.|\\.[0-9]+)$", regex_opts);
static strset_t whole = []() {
auto _v = get_whole_function_names();
auto _ret = _v;
for(std::string _ext : { "64", "_l", "_r" })
for(const auto& itr : _v)
_ret.emplace(itr + _ext);
return _ret;
}();
// don't instrument the functions when key is found anywhere in function name
if(std::regex_search(function_name, exclude) ||
std::regex_search(function_name, exclude_cxx))
{
_report("Excluding", "critical", 3);
return false;
}
if(whole.count(function_name) > 0)
{
_report("Excluding", "critical", 3);
return false;
}
// don't instrument the functions when key is found at the start of the function name
if(std::regex_search(function_name, leading))
{
_report("Excluding", "recommended", 3);
return false;
}
// don't instrument the functions when key is found at the end of the function name
if(std::regex_search(function_name, trailing))
{
_report("Excluding", "recommended", 3);
return false;
}
_report("Including", "no constraint", 2);
return true;
}
//======================================================================================//
// query_instr -- check whether there are one or more instrumentation points
//
@@ -2151,50 +2113,18 @@ query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc, flow_graph_t* cf
// Constraints for instrumentation. Returns true for those modules that
// shouldn't be instrumented.
bool
module_constraint(string_view_t fname)
module_constraint(const char*)
{
// fname is the name of module/file
string_t _fname = string_t{ fname };
// never instrumentat any module matching omnitrace
if(_fname.find("omnitrace") != string_t::npos) return true;
// always instrument these modules
if(_fname == "DEFAULT_MODULE" || _fname == "LIBRARY_MODULE") return false;
if(instrument_module(_fname)) return false;
// do not instrument
return true;
return false;
}
//======================================================================================//
// Constraint for routines. The constraint returns true for those routines that
// should not be instrumented.
bool
routine_constraint(string_view_t fname)
routine_constraint(const char*)
{
string_t _fname = string_t{ fname };
if(_fname.find("omnitrace") != string_t::npos) return true;
auto npos = std::string::npos;
if(_fname.find("FunctionInfo") != npos || _fname.find("_L_lock") != npos ||
_fname.find("_L_unlock") != npos)
return true; // Don't instrument
else
{
// Should the routine fname be instrumented?
if(instrument_entity(string_t(fname)))
{
// Yes it should be instrumented. Return false
return false;
}
else
{
// No. The selective instrumentation file says: don't instrument it
return true;
}
}
return false;
}
namespace
@@ -282,7 +282,42 @@ omnitrace_fork_callback(thread_t* parent, thread_t* child)
}
//
//======================================================================================//
// insert_instr -- generic insert instrumentation function
// insert_instr -- insert instrumentation into a function
//
template <typename Tp>
bool
insert_instr(address_space_t* mutatee, const bpvector_t<point_t*>& _points, Tp traceFunc,
procedure_loc_t traceLoc, bool allow_traps)
{
if(!traceFunc || _points.empty()) return false;
auto _trace = traceFunc.get();
auto _traps = std::set<point_t*>{};
if(!allow_traps)
{
for(const auto& itr : _points)
{
if(itr && itr->usesTrap_NP()) _traps.insert(itr);
}
}
size_t _n = 0;
for(const auto& itr : _points)
{
if(!itr || _traps.count(itr) > 0)
continue;
else if(traceLoc == BPatch_entry)
mutatee->insertSnippet(*_trace, *itr, BPatch_callBefore, BPatch_firstSnippet);
else
mutatee->insertSnippet(*_trace, *itr);
++_n;
}
return (_n > 0);
}
//
//======================================================================================//
// insert_instr -- insert instrumentation into loops
//
template <typename Tp>
bool
@@ -311,27 +346,6 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
if(_points == nullptr) return false;
if(_points->empty()) return false;
/*if(loop_level_instr)
{
flow_graph_t* flow = funcToInstr->getCFG();
bpvector_t<basic_loop_t*> basicLoop;
flow->getOuterLoops(basicLoop);
for(auto litr = basicLoop.begin(); litr != basicLoop.end(); ++litr)
{
bpvector_t<point_t*>* _tmp;
if(traceLoc == BPatch_entry)
_tmp = cfGraph->findLoopInstPoints(BPatch_locLoopEntry, *litr);
else if(traceLoc == BPatch_exit)
_tmp = cfGraph->findLoopInstPoints(BPatch_locLoopExit, *litr);
if(!_tmp)
continue;
for(auto& itr : *_tmp)
_points->push_back(itr);
}
}*/
// verbprintf(0, "Instrumenting |> [ %s ]\n", name.m_name.c_str());
std::set<point_t*> _traps{};
if(!allow_traps)
{
@@ -348,9 +362,6 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
continue;
else if(traceLoc == BPatch_entry)
mutatee->insertSnippet(*_trace, *itr, BPatch_callBefore, BPatch_firstSnippet);
// else if(traceLoc == BPatch_exit)
// mutatee->insertSnippet(*_trace, *itr, BPatch_callAfter,
// BPatch_firstSnippet);
else
mutatee->insertSnippet(*_trace, *itr);
++_n;
@@ -358,3 +369,46 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
return (_n > 0);
}
//
//======================================================================================//
// insert_instr -- insert instrumentation into basic blocks
//
template <typename Tp>
bool
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
basic_block_t* basicBlock, bool allow_traps)
{
point_t* _point = nullptr;
auto _trace = traceFunc.get();
basic_block_t* _bb = basicBlock;
switch(traceLoc)
{
case BPatch_entry: _point = _bb->findEntryPoint(); break;
case BPatch_exit: _point = _bb->findExitPoint(); break;
default:
verbprintf(0, "Warning! trace location type %i not supported\n",
(int) traceLoc);
return false;
}
if(_point == nullptr) return false;
if(!allow_traps && _point->usesTrap_NP()) return false;
switch(traceLoc)
{
case BPatch_entry:
return (mutatee->insertSnippet(*_trace, *_point, BPatch_callBefore,
BPatch_firstSnippet) != nullptr);
case BPatch_exit: return (mutatee->insertSnippet(*_trace, *_point) != nullptr);
default:
{
verbprintf(0, "Warning! trace location type %i not supported\n",
(int) traceLoc);
return false;
}
}
return false;
}
@@ -4,7 +4,7 @@ function(OMNITRACE_ADD_BIN_TEST)
TEST
"" # options
"NAME;TARGET;TIMEOUT;WORKING_DIRECTORY" # single value args
"ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGULAR_EXPRESSION;FAIL_REGULAR_EXPRESSION;SKIP_REGULAR_EXPRESSION;DEPENDS;COMMAND" # multiple
"ARGS;ENVIRONMENT;LABELS;PROPERTIES;PASS_REGEX;FAIL_REGEX;SKIP_REGEX;DEPENDS;COMMAND" # multiple
# value args
${ARGN})
@@ -50,11 +50,11 @@ function(OMNITRACE_ADD_BIN_TEST)
LABELS
"omnitrace-bin;${TEST_LABELS}"
PASS_REGULAR_EXPRESSION
"${TEST_PASS_REGULAR_EXPRESSION}"
"${TEST_PASS_REGEX}"
FAIL_REGULAR_EXPRESSION
"${TEST_FAIL_REGULAR_EXPRESSION}"
"${TEST_FAIL_REGEX}"
SKIP_REGULAR_EXPRESSION
"${TEST_SKIP_REGULAR_EXPRESSION}"
"${TEST_SKIP_REGEX}"
${TEST_PROPERTIES})
elseif(TARGET ${TEST_TARGET})
add_test(
@@ -73,11 +73,11 @@ function(OMNITRACE_ADD_BIN_TEST)
LABELS
"omnitrace-bin;${TEST_LABELS}"
PASS_REGULAR_EXPRESSION
"${TEST_PASS_REGULAR_EXPRESSION}"
"${TEST_PASS_REGEX}"
FAIL_REGULAR_EXPRESSION
"${TEST_FAIL_REGULAR_EXPRESSION}"
"${TEST_FAIL_REGEX}"
SKIP_REGULAR_EXPRESSION
"${TEST_SKIP_REGULAR_EXPRESSION}"
"${TEST_SKIP_REGEX}"
${TEST_PROPERTIES})
elseif(OMNITRACE_BUILD_TESTING)
message(FATAL_ERROR "Error! ${TEST_TARGET} does not exist")
@@ -90,7 +90,7 @@ omnitrace_add_bin_test(
ARGS --help
LABELS omnitrace-exe
TIMEOUT 45
PASS_REGULAR_EXPRESSION
PASS_REGEX
".*\\\[omnitrace\\\] Usage:.*\\\[DEBUG OPTIONS\\\].*\\\[MODE OPTIONS\\\].*\\\[LIBRARY OPTIONS\\\].*\\\[SYMBOL SELECTION OPTIONS\\\].*\\\[RUNTIME OPTIONS\\\].*\\\[GRANULARITY OPTIONS\\\].*\\\[DYNINST OPTIONS\\\].*"
)
@@ -115,7 +115,7 @@ omnitrace_add_bin_test(
WORKING_DIRECTORY
${PROJECT_BINARY_DIR}/omnitrace-tests-output/omnitrace-exe-simulate-ls
TIMEOUT 60
PASS_REGULAR_EXPRESSION
PASS_REGEX
".*available-instr.json.*available-instr.txt.*available-instr.xml.*excluded-instr.json.*excluded-instr.txt.*excluded-instr.xml.*instrumented-instr.json.*instrumented-instr.txt.*instrumented-instr.xml.*overlapping-instr.json.*overlapping-instr.txt.*overlapping-instr.xml.*"
)
@@ -125,7 +125,7 @@ omnitrace_add_bin_test(
ARGS --help
LABELS omnitrace-avail
TIMEOUT 45
PASS_REGULAR_EXPRESSION
PASS_REGEX
".*\\\[omnitrace-avail\\\] Usage:.*\\\[CATEGORIES\\\].*\\\[VIEW OPTIONS\\\].*\\\[COLUMN OPTIONS\\\].*\\\[WIDTH OPTIONS\\\].*\\\[OUTPUT OPTIONS\\\].*"
)
@@ -135,7 +135,7 @@ omnitrace_add_bin_test(
ARGS -r wall_clock -C --available
LABELS omnitrace-avail
TIMEOUT 45
PASS_REGULAR_EXPRESSION
PASS_REGEX
"\\\|[-]+\\\|\n\\\|[ ]+COMPONENT[ ]+\\\|\n\\\|[-]+\\\|\n\\\| (wall_clock)[ ]+\\\|\n\\\| (sampling_wall_clock)[ ]+\\\|\n\\\|[-]+\\\|"
)
@@ -145,8 +145,8 @@ omnitrace_add_bin_test(
ARGS --categories settings::omnitrace --brief
LABELS omnitrace-avail
TIMEOUT 45
PASS_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)"
FAIL_REGULAR_EXPRESSION
PASS_REGEX "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)"
FAIL_REGEX
"OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
)
@@ -156,6 +156,6 @@ omnitrace_add_bin_test(
ARGS --categories settings::timemory --brief
LABELS omnitrace-avail
TIMEOUT 45
PASS_REGULAR_EXPRESSION
PASS_REGEX
"OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
FAIL_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE)")
FAIL_REGEX "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE)")