User API + reorganized lib folders (#30)
* User API + reorganized lib folders
- omnitrace_user_start_trace
- omnitrace_user_stop_trace
- omnitrace_user_start_thread_trace
- omnitrace_user_stop_thread_trace
- omnitrace_user_push_region
- omnitrace_user_pop_region
* New OpenMP examples/tests
* Fix to KokkosP
* OMPT support
- fixed omnitrace instrumenting reporting
- common invoke improvements
- component::user_region
* exclude kmp_threadprivate_
* Separate omnitrace into multiple files
* PTL and timemory submodule updates
* Active guards + USE_OMPT guards in omnitrace-dl
* Tweak transpose default iterations
* omnitrace-precommit build target
* Omnitrace exe restructuring pt 2
- Never instrument functions with less than 4 instructions
- Never instrument ompt_start_tool or nanosleep
- module_function serializes heuristics
- removed hash stuff from omnitrace
- removed instr_procedures lambda
- WAITPID_DEBUG_MESSAGE
* set_state, "_hidden" fix, CI exceptions, backtrace fix
- set_state function
- fixed "_hidden" from appearing in print macros using __FUNCTION__
- OMNITRACE_CI_THROW
- more CI checks in library
- fixed backtrace init value sample issue being ignored
* Tweaks to OMPT tests
* cmake-formatting
* Removed debug output from backtrace processing
* Fix warnings and verbosity
* omnitrace-dl fix for libomp
* omnitrace-avail fixes
- remove second omnitrace_init_library call
- fix -r option not working
* Additional testing
- source/bin/tests
- tests for omnitrace-exe
- tests for omnitrace-avail
* cmake-format
* Reduce runtime of openmp-lu
* Update openmp-lu and tests timeout
* openmp-lu and CI tweaks
- decrease iterations
- OMP_NUM_THREADS=2
- install clang and libomp-dev in linux-ci
- fix data-files in linux-ci
[ROCm/rocprofiler-systems commit: d80752bc69]
This commit is contained in:
committed by
GitHub
parent
a23bf28aaa
commit
083035dd8b
@@ -1,3 +1,7 @@
|
||||
add_subdirectory(omnitrace-avail)
|
||||
add_subdirectory(omnitrace-critical-trace)
|
||||
add_subdirectory(omnitrace)
|
||||
|
||||
if(OMNITRACE_BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "library/components/omnitrace.hpp"
|
||||
#include "library/components/pthread_gotcha.hpp"
|
||||
#include "library/components/roctracer.hpp"
|
||||
#include "library/components/user_region.hpp"
|
||||
#include "library/config.hpp"
|
||||
|
||||
#include <timemory/components.hpp>
|
||||
@@ -613,8 +614,6 @@ main(int argc, char** argv)
|
||||
|
||||
if(!os) os = &std::cout;
|
||||
|
||||
omnitrace_init_library();
|
||||
|
||||
if(include_components) write_component_info(*os, options, use_mark, fields);
|
||||
|
||||
dump_log();
|
||||
@@ -766,7 +765,9 @@ write_component_info(std::ostream& os, const array_t<bool, N>& options,
|
||||
_mark.at(i));
|
||||
}
|
||||
|
||||
_selected += (is_category_selected(std::get<2>(itr).at(CATEGORY))) ? 1 : 0;
|
||||
if(!category_regex_keys.empty())
|
||||
_selected +=
|
||||
(is_category_selected(std::get<2>(itr).at(CATEGORY))) ? 1 : 0;
|
||||
|
||||
if(_selected == 0) continue;
|
||||
}
|
||||
@@ -834,7 +835,8 @@ write_component_info(std::ostream& os, const array_t<bool, N>& options,
|
||||
_mark.at(i));
|
||||
}
|
||||
|
||||
_selected += (is_category_selected(std::get<2>(itr).at(CATEGORY))) ? 1 : 0;
|
||||
if(!category_regex_keys.empty())
|
||||
_selected += (is_category_selected(std::get<2>(itr).at(CATEGORY))) ? 1 : 0;
|
||||
|
||||
if(_selected > 0)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,19 @@
|
||||
#
|
||||
# ------------------------------------------------------------------------------#
|
||||
|
||||
add_executable(
|
||||
add_executable(omnitrace-exe ${_EXCLUDE})
|
||||
|
||||
target_sources(
|
||||
omnitrace-exe
|
||||
${_EXCLUDE} ${CMAKE_CURRENT_LIST_DIR}/omnitrace.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/omnitrace.hpp ${CMAKE_CURRENT_LIST_DIR}/details.cpp)
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/omnitrace.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/details.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/function_signature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/module_function.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/omnitrace.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/info.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/fwd.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/function_signature.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/module_function.hpp)
|
||||
|
||||
target_link_libraries(
|
||||
omnitrace-exe
|
||||
|
||||
@@ -40,7 +40,7 @@ get_whole_function_names()
|
||||
"backtrace", "backtrace_symbols", "backtrace_symbols_fd", "sigaddset",
|
||||
"sigandset", "sigdelset", "sigemptyset", "sigfillset", "sighold", "sigisemptyset",
|
||||
"sigismember", "sigorset", "sigrelse", "sigvec", "strtok", "strstr", "sbrk",
|
||||
"strxfrm",
|
||||
"strxfrm", "atexit", "ompt_start_tool", "nanosleep",
|
||||
// below are functions which never terminate
|
||||
"rocr::core::Signal::WaitAny", "rocr::core::Runtime::AsyncEventsLoop",
|
||||
"rocr::core::BusyWaitSignal::WaitAcquire",
|
||||
@@ -525,10 +525,32 @@ are_file_include_exclude_lists_empty()
|
||||
// the instrumented loop and formats it properly.
|
||||
//
|
||||
function_signature
|
||||
get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph,
|
||||
get_loop_file_line_info(module_t* module, procedure_t* func, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument)
|
||||
{
|
||||
if(!cfGraph || !loopToInstrument || !f) return function_signature{ "", "", "" };
|
||||
if(!cfGraph || !loopToInstrument || !func) return function_signature{ "", "", "" };
|
||||
|
||||
std::vector<BPatch_basicBlock*> basic_blocks{};
|
||||
loopToInstrument->getLoopBasicBlocksExclusive(basic_blocks);
|
||||
|
||||
if(basic_blocks.empty()) return function_signature{ "", "", "" };
|
||||
|
||||
auto base_addr = basic_blocks.front()->getStartAddress();
|
||||
auto last_addr = basic_blocks.front()->getEndAddress();
|
||||
basic_block_t* block = basic_blocks.front();
|
||||
for(const auto& itr : basic_blocks)
|
||||
{
|
||||
if(itr == block) continue;
|
||||
if(itr->dominates(block))
|
||||
{
|
||||
base_addr = itr->getStartAddress();
|
||||
last_addr = itr->getEndAddress();
|
||||
block = itr;
|
||||
}
|
||||
}
|
||||
|
||||
verbprintf(4, "Loop: size = %lu: base_addr = %lu, last_addr = %lu\n",
|
||||
(unsigned long) (last_addr - base_addr), base_addr, last_addr);
|
||||
|
||||
char fname[FUNCNAMELEN + 1];
|
||||
char mname[FUNCNAMELEN + 1];
|
||||
@@ -537,32 +559,14 @@ get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t*
|
||||
memset(fname, '\0', FUNCNAMELEN + 1);
|
||||
memset(mname, '\0', FUNCNAMELEN + 1);
|
||||
|
||||
mutatee_module->getName(mname, FUNCNAMELEN);
|
||||
module->getName(mname, FUNCNAMELEN);
|
||||
func->getName(fname, FUNCNAMELEN);
|
||||
|
||||
bpvector_t<point_t*>* loopStartInst =
|
||||
cfGraph->findLoopInstPoints(BPatch_locLoopStartIter, loopToInstrument);
|
||||
bpvector_t<point_t*>* loopExitInst =
|
||||
cfGraph->findLoopInstPoints(BPatch_locLoopEndIter, loopToInstrument);
|
||||
auto* returnType = func->getReturnType();
|
||||
|
||||
if(!loopStartInst || !loopExitInst) return function_signature{ "", "", "" };
|
||||
if(returnType) typeName = returnType->getName();
|
||||
|
||||
unsigned long baseAddr = (unsigned long) (*loopStartInst)[0]->getAddress();
|
||||
unsigned long lastAddr =
|
||||
(unsigned long) (*loopExitInst)[loopExitInst->size() - 1]->getAddress();
|
||||
verbprintf(3, "Loop: size of lastAddr = %lu: baseAddr = %lu, lastAddr = %lu\n",
|
||||
(unsigned long) loopExitInst->size(), (unsigned long) baseAddr,
|
||||
(unsigned long) lastAddr);
|
||||
|
||||
f->getName(fname, FUNCNAMELEN);
|
||||
|
||||
auto* returnType = f->getReturnType();
|
||||
|
||||
if(returnType)
|
||||
{
|
||||
typeName = returnType->getName();
|
||||
}
|
||||
|
||||
auto* params = f->getParams();
|
||||
auto* params = func->getParams();
|
||||
std::vector<string_t> _params;
|
||||
if(params)
|
||||
{
|
||||
@@ -574,36 +578,51 @@ get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t*
|
||||
}
|
||||
}
|
||||
|
||||
bpvector_t<BPatch_statement> lines;
|
||||
bpvector_t<BPatch_statement> linesEnd;
|
||||
bpvector_t<BPatch_statement> lines{};
|
||||
bpvector_t<BPatch_statement> linesEnd{};
|
||||
|
||||
bool info1 = mutatee_module->getSourceLines(baseAddr, lines);
|
||||
bool info1 = module->getSourceLines(base_addr, lines);
|
||||
|
||||
string_t filename = mname;
|
||||
|
||||
if(info1)
|
||||
{
|
||||
// filename = lines[0].fileName();
|
||||
auto row1 = lines[0].lineNumber();
|
||||
auto col1 = lines[0].lineOffset();
|
||||
int row1 = 0;
|
||||
int col1 = 0;
|
||||
for(auto& itr : lines)
|
||||
{
|
||||
if(itr.lineNumber() > 0)
|
||||
{
|
||||
row1 = itr.lineNumber();
|
||||
col1 = itr.lineOffset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(row1 == 0 && col1 == 0)
|
||||
return function_signature(typeName, fname, filename, _params);
|
||||
|
||||
int row2 = 0;
|
||||
int col2 = 0;
|
||||
for(auto& itr : lines)
|
||||
{
|
||||
row2 = std::max(row2, itr.lineNumber());
|
||||
col2 = std::max(col2, itr.lineOffset());
|
||||
}
|
||||
|
||||
if(col1 < 0) col1 = 0;
|
||||
|
||||
// This following section is attempting to remedy the limitations of
|
||||
// getSourceLines for loops. As the program goes through the loop, the resulting
|
||||
// lines go from the loop head, through the instructions present in the loop, to
|
||||
// the last instruction in the loop, back to the loop head, then to the next
|
||||
// instruction outside of the loop. What this section does is starts at the last
|
||||
// instruction in the loop, then goes through the addresses until it reaches the
|
||||
// next instruction outside of the loop. We then bump back a line. This is not a
|
||||
// perfect solution, but we will work with the Dyninst team to find something
|
||||
// better.
|
||||
bool info2 = mutatee_module->getSourceLines((unsigned long) lastAddr, linesEnd);
|
||||
verbprintf(3, "size of linesEnd = %lu\n", (unsigned long) linesEnd.size());
|
||||
bool info2 = module->getSourceLines(last_addr, linesEnd);
|
||||
verbprintf(4, "size of linesEnd = %lu\n", (unsigned long) linesEnd.size());
|
||||
|
||||
if(info2)
|
||||
{
|
||||
auto row2 = linesEnd[0].lineNumber();
|
||||
auto col2 = linesEnd[0].lineOffset();
|
||||
for(auto& itr : linesEnd)
|
||||
{
|
||||
row2 = std::max(row2, itr.lineNumber());
|
||||
col2 = std::max(col2, itr.lineOffset());
|
||||
}
|
||||
if(col2 < 0) col2 = 0;
|
||||
if(row2 < row1) row1 = row2; // Fix for wrong line numbers
|
||||
|
||||
@@ -627,35 +646,30 @@ get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t*
|
||||
// We create a new name that embeds the file and line information in the name
|
||||
//
|
||||
function_signature
|
||||
get_func_file_line_info(module_t* mutatee_module, procedure_t* f)
|
||||
get_func_file_line_info(module_t* module, procedure_t* func)
|
||||
{
|
||||
bool info1, info2;
|
||||
unsigned long baseAddr, lastAddr;
|
||||
char fname[FUNCNAMELEN + 1];
|
||||
char mname[FUNCNAMELEN + 1];
|
||||
int row1, col1, row2, col2;
|
||||
string_t filename = {};
|
||||
string_t typeName = {};
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
char fname[FUNCNAMELEN + 1];
|
||||
char mname[FUNCNAMELEN + 1];
|
||||
string_t typeName = {};
|
||||
|
||||
memset(fname, '\0', FUNCNAMELEN + 1);
|
||||
memset(mname, '\0', FUNCNAMELEN + 1);
|
||||
|
||||
mutatee_module->getName(mname, FUNCNAMELEN);
|
||||
module->getName(mname, FUNCNAMELEN);
|
||||
func->getName(fname, FUNCNAMELEN);
|
||||
|
||||
baseAddr = (unsigned long) (f->getBaseAddr());
|
||||
f->getAddressRange(baseAddr, lastAddr);
|
||||
bpvector_t<BPatch_statement> lines;
|
||||
f->getName(fname, FUNCNAMELEN);
|
||||
address_t base_addr{};
|
||||
address_t last_addr{};
|
||||
func->getAddressRange(base_addr, last_addr);
|
||||
|
||||
auto* returnType = f->getReturnType();
|
||||
auto* returnType = func->getReturnType();
|
||||
|
||||
if(returnType)
|
||||
{
|
||||
typeName = returnType->getName();
|
||||
}
|
||||
if(returnType) typeName = returnType->getName();
|
||||
|
||||
auto* params = f->getParams();
|
||||
std::vector<string_t> _params;
|
||||
auto* params = func->getParams();
|
||||
std::vector<string_t> _params = {};
|
||||
if(params)
|
||||
{
|
||||
for(auto* itr : *params)
|
||||
@@ -666,32 +680,16 @@ get_func_file_line_info(module_t* mutatee_module, procedure_t* f)
|
||||
}
|
||||
}
|
||||
|
||||
info1 = mutatee_module->getSourceLines((unsigned long) baseAddr, lines);
|
||||
bpvector_t<BPatch_statement> lines = {};
|
||||
bool info = module->getSourceLines(base_addr, lines);
|
||||
|
||||
filename = mname;
|
||||
string_t filename = mname;
|
||||
|
||||
if(info1)
|
||||
if(info && !lines.empty())
|
||||
{
|
||||
// filename = lines[0].fileName();
|
||||
row1 = lines[0].lineNumber();
|
||||
col1 = lines[0].lineOffset();
|
||||
|
||||
if(col1 < 0) col1 = 0;
|
||||
info2 = mutatee_module->getSourceLines((unsigned long) (lastAddr - 1), lines);
|
||||
if(info2)
|
||||
{
|
||||
row2 = lines[1].lineNumber();
|
||||
col2 = lines[1].lineOffset();
|
||||
if(col2 < 0) col2 = 0;
|
||||
if(row2 < row1) row1 = row2;
|
||||
return function_signature(typeName, fname, filename, _params, { row1, 0 },
|
||||
{ 0, 0 }, false, info1, info2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return function_signature(typeName, fname, filename, _params, { row1, 0 },
|
||||
{ 0, 0 }, false, info1, info2);
|
||||
}
|
||||
auto row = lines.front().lineNumber();
|
||||
return function_signature(typeName, fname, filename, _params, { row, 0 },
|
||||
{ 0, 0 }, false, info, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "function_signature.hpp"
|
||||
|
||||
function_signature::function_signature(string_t _ret, const string_t& _name,
|
||||
string_t _file, location_t _row, location_t _col,
|
||||
bool _loop, bool _info_beg, bool _info_end)
|
||||
: m_loop(_loop)
|
||||
, m_info_beg(_info_beg)
|
||||
, m_info_end(_info_end)
|
||||
, m_row(std::move(_row))
|
||||
, m_col(std::move(_col))
|
||||
, m_return(std::move(_ret))
|
||||
, m_name(tim::demangle(_name))
|
||||
, m_file(std::move(_file))
|
||||
{
|
||||
if(m_file.find('/') != string_t::npos)
|
||||
m_file = m_file.substr(m_file.find_last_of('/') + 1);
|
||||
}
|
||||
|
||||
function_signature::function_signature(const string_t& _ret, const string_t& _name,
|
||||
const string_t& _file,
|
||||
const std::vector<string_t>& _params,
|
||||
location_t _row, location_t _col, bool _loop,
|
||||
bool _info_beg, bool _info_end)
|
||||
: function_signature(_ret, _name, _file, _row, _col, _loop, _info_beg, _info_end)
|
||||
{
|
||||
m_params = "(";
|
||||
for(const auto& itr : _params)
|
||||
m_params.append(itr + ", ");
|
||||
if(!_params.empty()) m_params = m_params.substr(0, m_params.length() - 2);
|
||||
m_params += ")";
|
||||
}
|
||||
|
||||
string_t
|
||||
function_signature::get(function_signature& sig)
|
||||
{
|
||||
return sig.get();
|
||||
}
|
||||
|
||||
string_t
|
||||
function_signature::get() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
if(use_return_info && !m_return.empty()) ss << m_return << " ";
|
||||
ss << m_name;
|
||||
if(use_args_info) ss << m_params;
|
||||
if(m_loop && m_info_beg)
|
||||
{
|
||||
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!");
|
||||
}
|
||||
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 << "]";
|
||||
|
||||
m_signature = ss.str();
|
||||
return m_signature;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fwd.hpp"
|
||||
|
||||
struct function_signature
|
||||
{
|
||||
using location_t = std::pair<unsigned long, unsigned long>;
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(function_signature)
|
||||
|
||||
function_signature(string_t _ret, const string_t& _name, string_t _file,
|
||||
location_t _row = { 0, 0 }, location_t _col = { 0, 0 },
|
||||
bool _loop = false, bool _info_beg = false,
|
||||
bool _info_end = false);
|
||||
|
||||
function_signature(const string_t& _ret, const string_t& _name, const string_t& _file,
|
||||
const std::vector<string_t>& _params, location_t _row = { 0, 0 },
|
||||
location_t _col = { 0, 0 }, bool _loop = false,
|
||||
bool _info_beg = false, bool _info_end = false);
|
||||
|
||||
static string_t get(function_signature& sig);
|
||||
string_t get() const;
|
||||
|
||||
bool m_loop = false;
|
||||
bool m_info_beg = false;
|
||||
bool m_info_end = false;
|
||||
location_t m_row = { 0, 0 };
|
||||
location_t m_col = { 0, 0 };
|
||||
string_t m_return = {};
|
||||
string_t m_name = {};
|
||||
string_t m_params = "()";
|
||||
string_t m_file = {};
|
||||
mutable string_t m_signature = {};
|
||||
|
||||
friend bool operator==(const function_signature& lhs, const function_signature& rhs)
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& _ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
(void) get();
|
||||
_ar(cereal::make_nvp("loop", m_loop), cereal::make_nvp("info_beg", m_info_beg),
|
||||
cereal::make_nvp("info_end", m_info_end), cereal::make_nvp("row", m_row),
|
||||
cereal::make_nvp("col", m_col), cereal::make_nvp("return", m_return),
|
||||
cereal::make_nvp("name", m_name), cereal::make_nvp("params", m_params),
|
||||
cereal::make_nvp("file", m_file), cereal::make_nvp("signature", m_signature));
|
||||
(void) get();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,273 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/policy.hpp>
|
||||
#include <timemory/tpls/cereal/archives.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/popen.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
#include <BPatch.h>
|
||||
#include <BPatch_Vector.h>
|
||||
#include <BPatch_addressSpace.h>
|
||||
#include <BPatch_basicBlock.h>
|
||||
#include <BPatch_basicBlockLoop.h>
|
||||
#include <BPatch_callbacks.h>
|
||||
#include <BPatch_function.h>
|
||||
#include <BPatch_instruction.h>
|
||||
#include <BPatch_point.h>
|
||||
#include <BPatch_process.h>
|
||||
#include <BPatch_snippet.h>
|
||||
#include <BPatch_statement.h>
|
||||
#include <Instruction.h>
|
||||
#include <dyntypes.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#define MUTNAMELEN 1024
|
||||
#define FUNCNAMELEN 32 * 1024
|
||||
#define NO_ERROR -1
|
||||
#define TIMEMORY_BIN_DIR "bin"
|
||||
|
||||
#if !defined(PATH_MAX)
|
||||
# define PATH_MAX std::numeric_limits<int>::max();
|
||||
#endif
|
||||
|
||||
struct function_signature;
|
||||
struct module_function;
|
||||
|
||||
template <typename Tp>
|
||||
using bpvector_t = BPatch_Vector<Tp>;
|
||||
|
||||
using string_t = std::string;
|
||||
using string_view_t = std::string_view;
|
||||
using stringstream_t = std::stringstream;
|
||||
using strvec_t = std::vector<string_t>;
|
||||
using strset_t = std::set<string_t>;
|
||||
using regexvec_t = std::vector<std::regex>;
|
||||
using fmodset_t = std::set<module_function>;
|
||||
using fixed_modset_t = std::map<fmodset_t*, bool>;
|
||||
using exec_callback_t = BPatchExecCallback;
|
||||
using exit_callback_t = BPatchExitCallback;
|
||||
using fork_callback_t = BPatchForkCallback;
|
||||
using patch_t = BPatch;
|
||||
using process_t = BPatch_process;
|
||||
using thread_t = BPatch_thread;
|
||||
using binary_edit_t = BPatch_binaryEdit;
|
||||
using image_t = BPatch_image;
|
||||
using module_t = BPatch_module;
|
||||
using procedure_t = BPatch_function;
|
||||
using snippet_t = BPatch_snippet;
|
||||
using call_expr_t = BPatch_funcCallExpr;
|
||||
using address_space_t = BPatch_addressSpace;
|
||||
using flow_graph_t = BPatch_flowGraph;
|
||||
using basic_block_t = BPatch_basicBlock;
|
||||
using basic_loop_t = BPatch_basicBlockLoop;
|
||||
using procedure_loc_t = BPatch_procedureLocation;
|
||||
using point_t = BPatch_point;
|
||||
using local_var_t = BPatch_localVar;
|
||||
using const_expr_t = BPatch_constExpr;
|
||||
using error_level_t = BPatchErrorLevel;
|
||||
using patch_pointer_t = std::shared_ptr<patch_t>;
|
||||
using snippet_pointer_t = std::shared_ptr<snippet_t>;
|
||||
using call_expr_pointer_t = std::shared_ptr<call_expr_t>;
|
||||
using snippet_vec_t = bpvector_t<snippet_t*>;
|
||||
using procedure_vec_t = bpvector_t<procedure_t*>;
|
||||
using basic_block_set_t = std::set<basic_block_t*>;
|
||||
using basic_loop_vec_t = bpvector_t<basic_loop_t*>;
|
||||
using snippet_pointer_vec_t = std::vector<snippet_pointer_t>;
|
||||
using instruction_t = Dyninst::InstructionAPI::Instruction;
|
||||
|
||||
void
|
||||
omnitrace_prefork_callback(thread_t* parent, thread_t* child);
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Global Variables
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
// label settings
|
||||
//
|
||||
extern bool use_return_info;
|
||||
extern bool use_args_info;
|
||||
extern bool use_file_info;
|
||||
extern bool use_line_info;
|
||||
//
|
||||
// heuristic settings
|
||||
//
|
||||
extern bool allow_overlapping;
|
||||
extern bool loop_level_instr;
|
||||
extern bool instr_dynamic_callsites;
|
||||
extern bool instr_traps;
|
||||
extern bool instr_loop_traps;
|
||||
extern size_t min_address_range;
|
||||
extern size_t min_loop_address_range;
|
||||
extern size_t min_instructions;
|
||||
//
|
||||
// debug settings
|
||||
//
|
||||
extern bool werror;
|
||||
extern bool debug_print;
|
||||
extern int verbose_level;
|
||||
//
|
||||
// string settings
|
||||
//
|
||||
extern string_t main_fname;
|
||||
extern string_t argv0;
|
||||
extern string_t cmdv0;
|
||||
extern string_t default_components;
|
||||
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;
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
// control debug printf statements
|
||||
#define errprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(werror || LEVEL < 0) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[omnitrace][exe] Error! " __VA_ARGS__); \
|
||||
char _buff[FUNCNAMELEN]; \
|
||||
sprintf(_buff, "[omnitrace][exe] Error! " __VA_ARGS__); \
|
||||
throw std::runtime_error(std::string{ _buff }); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[omnitrace][exe] Warning! " __VA_ARGS__); \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
// control verbose printf statements
|
||||
#define verbprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stdout, "[omnitrace][exe] " __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define verbprintf_bare(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) fprintf(stdout, __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
template <typename... T>
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
strset_t
|
||||
get_whole_function_names();
|
||||
|
||||
function_signature
|
||||
get_func_file_line_info(module_t* mutatee_module, procedure_t* f);
|
||||
|
||||
function_signature
|
||||
get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument);
|
||||
|
||||
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);
|
||||
|
||||
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, procedure_t* funcToInstr, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, flow_graph_t* cfGraph = nullptr,
|
||||
basic_loop_t* loopToInstrument = nullptr, bool allow_traps = true);
|
||||
|
||||
void
|
||||
errorFunc(error_level_t level, int num, const char** params);
|
||||
|
||||
procedure_t*
|
||||
find_function(image_t* appImage, const string_t& functionName, const strset_t& = {});
|
||||
|
||||
void
|
||||
error_func_real(error_level_t level, int num, const char* const* params);
|
||||
|
||||
void
|
||||
error_func_fake(error_level_t level, int num, const char* const* params);
|
||||
@@ -0,0 +1,251 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fwd.hpp"
|
||||
#include "module_function.hpp"
|
||||
|
||||
static inline void
|
||||
dump_info(std::ostream& _os, const fmodset_t& _data)
|
||||
{
|
||||
module_function::reset_width();
|
||||
for(const auto& itr : _data)
|
||||
module_function::update_width(itr);
|
||||
|
||||
module_function::write_header(_os);
|
||||
for(const auto& itr : _data)
|
||||
_os << itr << '\n';
|
||||
|
||||
module_function::reset_width();
|
||||
}
|
||||
//
|
||||
template <typename ArchiveT,
|
||||
std::enable_if_t<tim::concepts::is_archive<ArchiveT>::value, int> = 0>
|
||||
static inline void
|
||||
dump_info(ArchiveT& _ar, const fmodset_t& _data)
|
||||
{
|
||||
_ar(tim::cereal::make_nvp("module_functions", _data));
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _label, string_t _oname, const string_t& _ext,
|
||||
const fmodset_t& _data, int _level, bool _fail)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
_oname += "." + _ext;
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[dump_info] Error opening '" << _oname << " for output";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
if(_fail)
|
||||
throw std::runtime_error(std::string{ "[omnitrace][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(!debug_print && verbose_level < _level) return;
|
||||
|
||||
if(_ext == "txt")
|
||||
{
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
dump_info(ofs, _data);
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "xml")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::XMLOutputArchive>;
|
||||
output_policy::indent() = true;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::PrettyJSONOutputArchive>;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[omnitrace][exe] Error in ", __FUNCTION__, " :: filename '", _oname,
|
||||
"' does not have one of recognized file extensions: txt, json, xml"));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _oname, const fmodset_t& _data, int _level, bool _fail,
|
||||
const string_t& _type, const strset_t& _ext)
|
||||
{
|
||||
for(const auto& itr : _ext)
|
||||
dump_info(_type, _oname, itr, _data, _level, _fail);
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _label, const string_t& _iname, fmodset_t& _data, int _level)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
auto _pos = _iname.find_last_of('.');
|
||||
std::string _ext = {};
|
||||
if(_pos != std::string::npos) _ext = _iname.substr(_pos + 1, _iname.length());
|
||||
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[load_info] Error opening '" << _iname << " for input";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
throw std::runtime_error(std::string{ "[omnitrace][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(_ext == "xml")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::XMLInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::JSONInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[omnitrace][exe] Error in ", __FUNCTION__, " :: filename '", _iname,
|
||||
"' does not have one of recognized extentions: txt, json, xml :: ", _ext));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _inp, std::map<std::string, fmodset_t*>& _data, int _level)
|
||||
{
|
||||
std::vector<std::string> _exceptions{};
|
||||
_exceptions.reserve(_data.size());
|
||||
for(auto& itr : _data)
|
||||
{
|
||||
try
|
||||
{
|
||||
fmodset_t _tmp{};
|
||||
load_info(itr.first, _inp, _tmp, _level);
|
||||
// add to the existing
|
||||
itr.second->insert(_tmp.begin(), _tmp.end());
|
||||
// if it did not throw it was successfully loaded
|
||||
_exceptions.clear();
|
||||
break;
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
_exceptions.emplace_back(_e.what());
|
||||
}
|
||||
}
|
||||
if(!_exceptions.empty())
|
||||
{
|
||||
std::stringstream _msg{};
|
||||
for(auto& itr : _exceptions)
|
||||
{
|
||||
_msg << "[omnitrace][exe] " << itr << "\n";
|
||||
}
|
||||
throw std::runtime_error(_msg.str());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "module_function.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "omnitrace.hpp"
|
||||
|
||||
module_function::width_t&
|
||||
module_function::get_width()
|
||||
{
|
||||
static width_t _instance = []() {
|
||||
width_t _tmp;
|
||||
_tmp.fill(0);
|
||||
return _tmp;
|
||||
}();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void
|
||||
module_function::reset_width()
|
||||
{
|
||||
get_width().fill(0);
|
||||
}
|
||||
|
||||
void
|
||||
module_function::update_width(const module_function& rhs)
|
||||
{
|
||||
get_width()[0] = std::max<size_t>(get_width()[0], rhs.module_name.length());
|
||||
get_width()[1] = std::max<size_t>(get_width()[1], rhs.function_name.length());
|
||||
get_width()[2] = std::max<size_t>(get_width()[2], rhs.signature.get().length());
|
||||
}
|
||||
|
||||
module_function::module_function(module_t* mod, procedure_t* proc)
|
||||
: module{ mod }
|
||||
, function{ proc }
|
||||
, flow_graph{ proc->getCFG() }
|
||||
{
|
||||
if(flow_graph)
|
||||
{
|
||||
flow_graph->getAllBasicBlocks(basic_blocks);
|
||||
flow_graph->getOuterLoops(loop_blocks);
|
||||
}
|
||||
|
||||
for(const auto& itr : basic_blocks)
|
||||
{
|
||||
std::vector<instruction_t> instructions{};
|
||||
itr->getInstructions(instructions);
|
||||
num_instructions += instructions.size();
|
||||
}
|
||||
|
||||
char modname[FUNCNAMELEN];
|
||||
char fname[FUNCNAMELEN];
|
||||
module->getFullName(modname, FUNCNAMELEN);
|
||||
function->getName(fname, FUNCNAMELEN);
|
||||
module_name = modname;
|
||||
function_name = fname;
|
||||
signature = get_func_file_line_info(module, function);
|
||||
|
||||
if(!function->isInstrumentable())
|
||||
{
|
||||
verbprintf(0,
|
||||
"Warning! module function generated for un-instrumentable "
|
||||
"function: %s [%s]\n",
|
||||
function_name.c_str(), module_name.c_str());
|
||||
}
|
||||
std::pair<address_t, address_t> _range{};
|
||||
if(function->getAddressRange(_range.first, _range.second))
|
||||
address_range = _range.second - _range.first;
|
||||
}
|
||||
|
||||
void
|
||||
module_function::write_header(std::ostream& os)
|
||||
{
|
||||
auto w0 = std::min<size_t>(get_width()[0], absolute_max_width);
|
||||
auto w1 = std::min<size_t>(get_width()[1], absolute_max_width);
|
||||
auto w2 = std::min<size_t>(get_width()[2], absolute_max_width);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::setw(14) << "AddressRange"
|
||||
<< " " << std::setw(14) << "#Instructions"
|
||||
<< " " << std::setw(6) << "Ratio"
|
||||
<< " " << std::setw(w0 + 8) << std::left << "Module"
|
||||
<< " " << std::setw(w1 + 8) << std::left << "Function"
|
||||
<< " " << std::setw(w2 + 8) << std::left << "FunctionSignature"
|
||||
<< "\n";
|
||||
os << ss.str();
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::should_instrument() const
|
||||
{
|
||||
// hard constraints
|
||||
if(!is_instrumentable()) return false;
|
||||
if(!can_instrument_entry()) return false;
|
||||
if(!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;
|
||||
if(num_instructions < absolute_min_instructions)
|
||||
{
|
||||
messages.emplace_back(
|
||||
2, "Skipping", "function",
|
||||
TIMEMORY_JOIN("-", "less-than", absolute_min_instructions, "instructions"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if(is_entry_trap_constrained()) return false;
|
||||
if(is_exit_trap_constrained()) return false;
|
||||
|
||||
// needs to be applied before address range and number of instruction constraints
|
||||
if(is_dynamic_callsite_forced()) return true;
|
||||
|
||||
if(is_address_range_constrained()) return false;
|
||||
if(is_num_instructions_constrained()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_instrumentable() const
|
||||
{
|
||||
if(!function->isInstrumentable())
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "module", "not-instrumentable");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool
|
||||
check_regex_restrictions(const std::string& _name, const regexvec_t& _regexes)
|
||||
{
|
||||
// NOLINTNEXTLINE
|
||||
for(auto& itr : _regexes)
|
||||
if(std::regex_search(_name, itr)) return true;
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool
|
||||
module_function::is_user_restricted() const
|
||||
{
|
||||
if(!file_restrict.empty())
|
||||
{
|
||||
if(check_regex_restrictions(module_name, file_restrict))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "module", "module-restrict-regex");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "module", "module-restrict-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!func_restrict.empty())
|
||||
{
|
||||
if(check_regex_restrictions(module_name, func_restrict))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "function", "function-restrict-regex");
|
||||
return false;
|
||||
}
|
||||
else if(check_regex_restrictions(signature.get(), func_restrict))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "function", "function-restrict-regex");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function", "function-restrict-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_user_included() const
|
||||
{
|
||||
if(!file_include.empty())
|
||||
{
|
||||
if(check_regex_restrictions(module_name, file_include))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "module", "module-include-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!func_include.empty())
|
||||
{
|
||||
if(check_regex_restrictions(function_name, func_include))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "function", "function-include-regex");
|
||||
return true;
|
||||
}
|
||||
else if(check_regex_restrictions(signature.get(), func_include))
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "function", "function-include-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_user_excluded() const
|
||||
{
|
||||
if(!file_exclude.empty())
|
||||
{
|
||||
if(check_regex_restrictions(module_name, file_exclude))
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "module", "module-exclude-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!func_exclude.empty())
|
||||
{
|
||||
if(check_regex_restrictions(function_name, func_exclude))
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "function-exclude-regex");
|
||||
return true;
|
||||
}
|
||||
else if(check_regex_restrictions(signature.get(), func_exclude))
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "function-exclude-regex");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_overlapping() const
|
||||
{
|
||||
procedure_vec_t _overlapping{};
|
||||
return function->findOverlapping(_overlapping);
|
||||
}
|
||||
|
||||
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");
|
||||
return true;
|
||||
}
|
||||
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");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_overlapping_constrained() const
|
||||
{
|
||||
if(!allow_overlapping && is_overlapping())
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "overlapping");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::contains_dynamic_callsites() const
|
||||
{
|
||||
if(flow_graph) return flow_graph->containsDynamicCallsites();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_dynamic_callsite_forced() const
|
||||
{
|
||||
if(instr_dynamic_callsites && contains_dynamic_callsites())
|
||||
{
|
||||
messages.emplace_back(2, "Forcing", "function", "dynamic-callsites");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_address_range_constrained() const
|
||||
{
|
||||
if(!loop_blocks.empty()) return is_loop_address_range_constrained();
|
||||
|
||||
if(address_range < min_address_range)
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "min-address-range");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_loop_address_range_constrained() const
|
||||
{
|
||||
if(loop_blocks.empty()) return false;
|
||||
|
||||
if(address_range < min_loop_address_range)
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "min-address-range-loop");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_num_instructions_constrained() const
|
||||
{
|
||||
if(num_instructions < min_instructions)
|
||||
{
|
||||
messages.emplace_back(2, "Skipping", "function", "min-instructions");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::can_instrument_entry() const
|
||||
{
|
||||
size_t _num_points = 0;
|
||||
size_t _num_traps = 0;
|
||||
|
||||
std::tie(_num_points, _num_traps) = query_instr(function, BPatch_entry);
|
||||
|
||||
if(_num_points == 0)
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function", "no-instrumentable-entry-point");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::can_instrument_exit() const
|
||||
{
|
||||
size_t _num_points = 0;
|
||||
size_t _num_traps = 0;
|
||||
|
||||
std::tie(_num_points, _num_traps) = query_instr(function, BPatch_exit);
|
||||
|
||||
if(_num_points == 0)
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function", "no-instrumentable-exit-point");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_entry_trap_constrained() const
|
||||
{
|
||||
if(instr_traps) return false;
|
||||
|
||||
size_t _num_points = 0;
|
||||
size_t _num_traps = 0;
|
||||
|
||||
std::tie(_num_points, _num_traps) = query_instr(function, BPatch_entry);
|
||||
|
||||
if(!instr_traps && (_num_points - _num_traps) == 0)
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function",
|
||||
"entry-point-trap-instrumentation");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
module_function::is_exit_trap_constrained() const
|
||||
{
|
||||
if(instr_traps) return false;
|
||||
|
||||
size_t _num_points = 0;
|
||||
size_t _num_traps = 0;
|
||||
|
||||
std::tie(_num_points, _num_traps) = query_instr(function, BPatch_exit);
|
||||
|
||||
if((_num_points - _num_traps) == 0)
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function",
|
||||
"exit-point-trap-instrumentation");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t>
|
||||
module_function::operator()(address_space_t* _addr_space, procedure_t* _entr_trace,
|
||||
procedure_t* _exit_trace) const
|
||||
{
|
||||
std::pair<size_t, size_t> _count = { 0, 0 };
|
||||
|
||||
auto _name = signature.get();
|
||||
auto _trace_entr = omnitrace_call_expr(_name.c_str());
|
||||
auto _trace_exit = omnitrace_call_expr(_name.c_str());
|
||||
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))
|
||||
{
|
||||
messages.emplace_back(1, "Instrumenting", "function", "no-constraint");
|
||||
++_count.first;
|
||||
}
|
||||
|
||||
for(auto* itr : loop_blocks)
|
||||
{
|
||||
if(!loop_level_instr) continue;
|
||||
|
||||
auto _is_constrained = [this](bool _v, const std::string& _label) {
|
||||
if(!_v)
|
||||
{
|
||||
messages.emplace_back(3, "Skipping", "function", _label);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
size_t _points = 0;
|
||||
size_t _ntraps = 0;
|
||||
std::tie(_points, _ntraps) = query_instr(function, BPatch_entry, flow_graph, itr);
|
||||
|
||||
if(_is_constrained(_points == 0, "no-instrumentable-loop-entry-point")) continue;
|
||||
if(_is_constrained(!instr_traps && (_points - _ntraps) == 0,
|
||||
"loop-entry-point-trap-instrumentation"))
|
||||
continue;
|
||||
|
||||
std::tie(_points, _ntraps) = query_instr(function, BPatch_exit, flow_graph, itr);
|
||||
|
||||
if(_is_constrained(_points == 0, "no-instrumentable-loop-exit-point")) continue;
|
||||
if(_is_constrained(!instr_traps && (_points - _ntraps) == 0,
|
||||
"loop-exit-point-trap-instrumentation"))
|
||||
continue;
|
||||
|
||||
auto lname = get_loop_file_line_info(module, function, flow_graph, itr);
|
||||
auto _lname = lname.get();
|
||||
|
||||
messages.emplace_back(1, "Loop Instrumenting", "function", "no-constraint");
|
||||
++_count.second;
|
||||
|
||||
auto _ltrace_entr = omnitrace_call_expr(_lname.c_str());
|
||||
auto _ltrace_exit = omnitrace_call_expr(_lname.c_str());
|
||||
auto _lentr = _ltrace_entr.get(_entr_trace);
|
||||
auto _lexit = _ltrace_exit.get(_exit_trace);
|
||||
|
||||
insert_instr(_addr_space, function, _lentr, BPatch_entry, flow_graph, itr,
|
||||
instr_loop_traps);
|
||||
insert_instr(_addr_space, function, _lexit, BPatch_exit, flow_graph, itr,
|
||||
instr_loop_traps);
|
||||
}
|
||||
|
||||
return _count;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/tpls/cereal/cereal/cereal.hpp>
|
||||
|
||||
struct module_function
|
||||
{
|
||||
using width_t = std::array<size_t, 4>;
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
static constexpr size_t absolute_max_width = 80;
|
||||
static width_t& get_width();
|
||||
static void reset_width();
|
||||
static void update_width(const module_function& rhs);
|
||||
static void write_header(std::ostream& os);
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(module_function)
|
||||
|
||||
module_function(module_t* mod, procedure_t* proc);
|
||||
|
||||
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;
|
||||
|
||||
// hard constraints
|
||||
bool is_instrumentable() const; // checks whether can instrument
|
||||
bool can_instrument_entry() const; // checks for entry points
|
||||
bool can_instrument_exit() const; // checks for exit points
|
||||
bool is_module_constrained() const; // checks module constraints
|
||||
bool is_routine_constrained() const; // checks function constraints
|
||||
|
||||
// user bypass of heuristics
|
||||
bool is_user_restricted() const; // checks user restrict regexes
|
||||
bool is_user_included() const; // checks user include regexes
|
||||
bool is_user_excluded() const; // checks user exclude regexes
|
||||
|
||||
// applied before dynamic-callsite constraint
|
||||
bool is_overlapping_constrained() const; // checks overlapping constrains
|
||||
bool is_entry_trap_constrained() const; // checks entry trap constraint
|
||||
bool is_exit_trap_constrained() const; // checks exit trap constraint
|
||||
|
||||
// applied before address range and # instruction constraints
|
||||
bool is_dynamic_callsite_forced() const; // checks dynamic callsites
|
||||
|
||||
// estimate the size/work of the function
|
||||
bool is_address_range_constrained() const; // checks address range constraint
|
||||
bool is_num_instructions_constrained() const; // check # instructions constraint
|
||||
|
||||
uint64_t address_range = 0;
|
||||
uint64_t num_instructions = 0;
|
||||
module_t* module = nullptr;
|
||||
procedure_t* function = nullptr;
|
||||
flow_graph_t* flow_graph = nullptr;
|
||||
string_t module_name = {};
|
||||
string_t function_name = {};
|
||||
function_signature signature = {};
|
||||
basic_block_set_t basic_blocks = {};
|
||||
basic_loop_vec_t loop_blocks = {};
|
||||
|
||||
using str_msg_t = std::tuple<int, string_t, string_t, string_t>;
|
||||
using str_msg_vec_t = std::vector<str_msg_t>;
|
||||
|
||||
mutable str_msg_vec_t messages = {};
|
||||
|
||||
bool is_overlapping() const; // checks if func overlaps
|
||||
|
||||
private:
|
||||
bool is_loop_address_range_constrained() const; // checks loop addr range constraint
|
||||
bool contains_dynamic_callsites() const;
|
||||
|
||||
public:
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& ar, const unsigned);
|
||||
|
||||
friend bool operator<(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return (lhs.module_name == rhs.module_name)
|
||||
? ((lhs.function_name == rhs.function_name)
|
||||
? (lhs.signature.get() < rhs.signature.get())
|
||||
: (lhs.function_name < rhs.function_name))
|
||||
: (lhs.module_name < rhs.module_name);
|
||||
}
|
||||
|
||||
friend bool operator==(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return std::tie(lhs.module_name, lhs.function_name, lhs.signature,
|
||||
lhs.address_range, lhs.num_instructions) ==
|
||||
std::tie(rhs.module_name, rhs.function_name, rhs.signature,
|
||||
rhs.address_range, rhs.num_instructions);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const module_function& rhs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
auto w0 = std::min<size_t>(get_width()[0], absolute_max_width);
|
||||
auto w1 = std::min<size_t>(get_width()[1], absolute_max_width);
|
||||
auto w2 = std::min<size_t>(get_width()[2], absolute_max_width);
|
||||
|
||||
auto _get_str = [](const std::string& _inc) {
|
||||
if(_inc.length() > absolute_max_width)
|
||||
return _inc.substr(0, absolute_max_width - 3) + "...";
|
||||
return _inc;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
ss << std::setw(14) << rhs.address_range << " "
|
||||
<< std::setw(14) << rhs.num_instructions << " "
|
||||
<< std::setw(6) << std::setprecision(2) << std::fixed << (rhs.address_range / static_cast<double>(rhs.num_instructions)) << " "
|
||||
<< std::setw(w0 + 8) << std::left << _get_str(rhs.module_name) << " "
|
||||
<< std::setw(w1 + 8) << std::left << _get_str(rhs.function_name) << " "
|
||||
<< std::setw(w2 + 8) << std::left << _get_str(rhs.signature.get());
|
||||
// clang-format on
|
||||
|
||||
os << ss.str();
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ArchiveT>
|
||||
void
|
||||
module_function::serialize(ArchiveT& ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
ar(cereal::make_nvp("address_range", address_range),
|
||||
cereal::make_nvp("instructions", num_instructions),
|
||||
cereal::make_nvp("module", module_name),
|
||||
cereal::make_nvp("function", function_name),
|
||||
cereal::make_nvp("signature", signature));
|
||||
|
||||
if constexpr(tim::concepts::is_output_archive<ArchiveT>::value)
|
||||
{
|
||||
ar.setNextName("heuristics");
|
||||
ar.startNode();
|
||||
ar(cereal::make_nvp("should_instrument", should_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()),
|
||||
cereal::make_nvp("contains_dynamic_callsites", contains_dynamic_callsites()),
|
||||
cereal::make_nvp("is_module_constrained", is_module_constrained()),
|
||||
cereal::make_nvp("is_routine_constrained", is_routine_constrained()),
|
||||
cereal::make_nvp("is_user_restricted", is_user_restricted()),
|
||||
cereal::make_nvp("is_user_included", is_user_included()),
|
||||
cereal::make_nvp("is_user_excluded", is_user_excluded()),
|
||||
cereal::make_nvp("is_overlapping_constrained", is_overlapping_constrained()),
|
||||
cereal::make_nvp("is_entry_trap_constrained", is_entry_trap_constrained()),
|
||||
cereal::make_nvp("is_exit_trap_constrained", is_exit_trap_constrained()),
|
||||
cereal::make_nvp("is_dynamic_callsite_forced", is_dynamic_callsite_forced()),
|
||||
cereal::make_nvp("is_address_range_constrained",
|
||||
is_address_range_constrained()),
|
||||
cereal::make_nvp("is_loop_address_range_constrained",
|
||||
is_loop_address_range_constrained()),
|
||||
cereal::make_nvp("is_num_instructions_constrained",
|
||||
is_num_instructions_constrained()));
|
||||
ar.finishNode();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,231 +22,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
#include <timemory/mpl/concepts.hpp>
|
||||
#include <timemory/mpl/policy.hpp>
|
||||
#include <timemory/tpls/cereal/archives.hpp>
|
||||
#include <timemory/tpls/cereal/cereal.hpp>
|
||||
#include <timemory/utility/argparse.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/popen.hpp>
|
||||
#include <timemory/variadic/macros.hpp>
|
||||
|
||||
#include <BPatch.h>
|
||||
#include <BPatch_Vector.h>
|
||||
#include <BPatch_addressSpace.h>
|
||||
#include <BPatch_basicBlockLoop.h>
|
||||
#include <BPatch_callbacks.h>
|
||||
#include <BPatch_function.h>
|
||||
#include <BPatch_point.h>
|
||||
#include <BPatch_process.h>
|
||||
#include <BPatch_snippet.h>
|
||||
#include <BPatch_statement.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#define MUTNAMELEN 1024
|
||||
#define FUNCNAMELEN 32 * 1024
|
||||
#define NO_ERROR -1
|
||||
#define TIMEMORY_BIN_DIR "bin"
|
||||
|
||||
#if !defined(PATH_MAX)
|
||||
# define PATH_MAX std::numeric_limits<int>::max();
|
||||
#endif
|
||||
|
||||
struct function_signature;
|
||||
struct module_function;
|
||||
|
||||
template <typename Tp>
|
||||
using bpvector_t = BPatch_Vector<Tp>;
|
||||
|
||||
using string_t = std::string;
|
||||
using stringstream_t = std::stringstream;
|
||||
using strvec_t = std::vector<string_t>;
|
||||
using strset_t = std::set<string_t>;
|
||||
using regexvec_t = std::vector<std::regex>;
|
||||
using fmodset_t = std::set<module_function>;
|
||||
using fixed_modset_t = std::map<fmodset_t*, bool>;
|
||||
using exec_callback_t = BPatchExecCallback;
|
||||
using exit_callback_t = BPatchExitCallback;
|
||||
using fork_callback_t = BPatchForkCallback;
|
||||
using patch_t = BPatch;
|
||||
using process_t = BPatch_process;
|
||||
using thread_t = BPatch_thread;
|
||||
using binary_edit_t = BPatch_binaryEdit;
|
||||
using image_t = BPatch_image;
|
||||
using module_t = BPatch_module;
|
||||
using procedure_t = BPatch_function;
|
||||
using snippet_t = BPatch_snippet;
|
||||
using call_expr_t = BPatch_funcCallExpr;
|
||||
using address_space_t = BPatch_addressSpace;
|
||||
using flow_graph_t = BPatch_flowGraph;
|
||||
using basic_loop_t = BPatch_basicBlockLoop;
|
||||
using procedure_loc_t = BPatch_procedureLocation;
|
||||
using point_t = BPatch_point;
|
||||
using local_var_t = BPatch_localVar;
|
||||
using const_expr_t = BPatch_constExpr;
|
||||
using error_level_t = BPatchErrorLevel;
|
||||
using patch_pointer_t = std::shared_ptr<patch_t>;
|
||||
using snippet_pointer_t = std::shared_ptr<snippet_t>;
|
||||
using call_expr_pointer_t = std::shared_ptr<call_expr_t>;
|
||||
using snippet_vec_t = bpvector_t<snippet_t*>;
|
||||
using procedure_vec_t = bpvector_t<procedure_t*>;
|
||||
using basic_loop_vec_t = bpvector_t<basic_loop_t*>;
|
||||
using snippet_pointer_vec_t = std::vector<snippet_pointer_t>;
|
||||
|
||||
void
|
||||
omnitrace_prefork_callback(thread_t* parent, thread_t* child);
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Global Variables
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
// boolean settings
|
||||
//
|
||||
static bool use_return_info = false;
|
||||
static bool use_args_info = false;
|
||||
static bool use_file_info = false;
|
||||
static bool use_line_info = false;
|
||||
//
|
||||
// integral settings
|
||||
//
|
||||
extern bool debug_print;
|
||||
extern int verbose_level;
|
||||
//
|
||||
// string settings
|
||||
//
|
||||
static string_t main_fname = "main";
|
||||
static string_t argv0 = {};
|
||||
static string_t cmdv0 = {};
|
||||
static string_t default_components = "wall_clock";
|
||||
static string_t prefer_library = {};
|
||||
//
|
||||
// global variables
|
||||
//
|
||||
static patch_pointer_t bpatch = {};
|
||||
static call_expr_t* terminate_expr = nullptr;
|
||||
static snippet_vec_t init_names = {};
|
||||
static snippet_vec_t fini_names = {};
|
||||
static fmodset_t available_module_functions = {};
|
||||
static fmodset_t instrumented_module_functions = {};
|
||||
static fmodset_t overlapping_module_functions = {};
|
||||
static fmodset_t excluded_module_functions = {};
|
||||
static fixed_modset_t fixed_module_functions = {};
|
||||
static regexvec_t func_include = {};
|
||||
static regexvec_t func_exclude = {};
|
||||
static regexvec_t file_include = {};
|
||||
static regexvec_t file_exclude = {};
|
||||
static regexvec_t file_restrict = {};
|
||||
static regexvec_t func_restrict = {};
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
// control debug printf statements
|
||||
#define errprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(werror || LEVEL < 0) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[omnitrace][exe] Error! " __VA_ARGS__); \
|
||||
char _buff[FUNCNAMELEN]; \
|
||||
sprintf(_buff, "[omnitrace][exe] Error! " __VA_ARGS__); \
|
||||
throw std::runtime_error(std::string{ _buff }); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stderr, "[omnitrace][exe] Warning! " __VA_ARGS__); \
|
||||
} \
|
||||
fflush(stderr); \
|
||||
}
|
||||
|
||||
// control verbose printf statements
|
||||
#define verbprintf(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stdout, "[omnitrace][exe] " __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
#define verbprintf_bare(LEVEL, ...) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) fprintf(stdout, __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
template <typename... T>
|
||||
void
|
||||
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(char* fname);
|
||||
bool routine_constraint(const char* fname);
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
strset_t
|
||||
get_whole_function_names();
|
||||
|
||||
function_signature
|
||||
get_func_file_line_info(module_t* mutatee_module, procedure_t* f);
|
||||
|
||||
function_signature
|
||||
get_loop_file_line_info(module_t* mutatee_module, procedure_t* f, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument);
|
||||
|
||||
bool
|
||||
query_instr(procedure_t* funcToInstr, procedure_loc_t traceLoc,
|
||||
flow_graph_t* cfGraph = nullptr, basic_loop_t* loopToInstrument = nullptr,
|
||||
bool allow_traps = true);
|
||||
|
||||
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);
|
||||
|
||||
void
|
||||
errorFunc(error_level_t level, int num, const char** params);
|
||||
|
||||
procedure_t*
|
||||
find_function(image_t* appImage, const string_t& functionName, const strset_t& = {});
|
||||
|
||||
void
|
||||
error_func_real(error_level_t level, int num, const char* const* params);
|
||||
|
||||
void
|
||||
error_func_fake(error_level_t level, int num, const char* const* params);
|
||||
#include "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "info.hpp"
|
||||
#include "module_function.hpp"
|
||||
|
||||
//======================================================================================//
|
||||
|
||||
@@ -285,465 +64,6 @@ to_lower(string_t s)
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
struct function_signature
|
||||
{
|
||||
using location_t = std::pair<unsigned long, unsigned long>;
|
||||
|
||||
bool m_loop = false;
|
||||
bool m_info_beg = false;
|
||||
bool m_info_end = false;
|
||||
location_t m_row = { 0, 0 };
|
||||
location_t m_col = { 0, 0 };
|
||||
string_t m_return = {};
|
||||
string_t m_name = {};
|
||||
string_t m_params = "()";
|
||||
string_t m_file = {};
|
||||
mutable string_t m_signature = {};
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(function_signature)
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& _ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
(void) get();
|
||||
_ar(cereal::make_nvp("loop", m_loop), cereal::make_nvp("info_beg", m_info_beg),
|
||||
cereal::make_nvp("info_end", m_info_end), cereal::make_nvp("row", m_row),
|
||||
cereal::make_nvp("col", m_col), cereal::make_nvp("return", m_return),
|
||||
cereal::make_nvp("name", m_name), cereal::make_nvp("params", m_params),
|
||||
cereal::make_nvp("file", m_file), cereal::make_nvp("signature", m_signature));
|
||||
(void) get();
|
||||
}
|
||||
|
||||
function_signature(string_t _ret, const string_t& _name, string_t _file,
|
||||
location_t _row = { 0, 0 }, location_t _col = { 0, 0 },
|
||||
bool _loop = false, bool _info_beg = false, bool _info_end = false)
|
||||
: m_loop(_loop)
|
||||
, m_info_beg(_info_beg)
|
||||
, m_info_end(_info_end)
|
||||
, m_row(std::move(_row))
|
||||
, m_col(std::move(_col))
|
||||
, m_return(std::move(_ret))
|
||||
, m_name(tim::demangle(_name))
|
||||
, m_file(std::move(_file))
|
||||
{
|
||||
if(m_file.find('/') != string_t::npos)
|
||||
m_file = m_file.substr(m_file.find_last_of('/') + 1);
|
||||
}
|
||||
|
||||
function_signature(const string_t& _ret, const string_t& _name, const string_t& _file,
|
||||
const std::vector<string_t>& _params, location_t _row = { 0, 0 },
|
||||
location_t _col = { 0, 0 }, bool _loop = false,
|
||||
bool _info_beg = false, bool _info_end = false)
|
||||
: function_signature(_ret, _name, _file, _row, _col, _loop, _info_beg, _info_end)
|
||||
{
|
||||
m_params = "(";
|
||||
for(const auto& itr : _params)
|
||||
m_params.append(itr + ", ");
|
||||
if(!_params.empty()) m_params = m_params.substr(0, m_params.length() - 2);
|
||||
m_params += ")";
|
||||
}
|
||||
|
||||
friend bool operator==(const function_signature& lhs, const function_signature& rhs)
|
||||
{
|
||||
return lhs.get() == rhs.get();
|
||||
}
|
||||
|
||||
static auto get(function_signature& sig) { return sig.get(); }
|
||||
|
||||
string_t get() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
if(use_return_info && !m_return.empty()) ss << m_return << " ";
|
||||
ss << m_name;
|
||||
if(use_args_info) ss << m_params;
|
||||
if(m_loop && m_info_beg)
|
||||
{
|
||||
if(m_info_end)
|
||||
{
|
||||
ss << " [{" << m_row.first << "," << m_col.first << "}-{" << m_row.second
|
||||
<< "," << m_col.second << "}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "[{" << m_row.first << "," << m_col.first << "}]";
|
||||
}
|
||||
}
|
||||
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 << "]";
|
||||
|
||||
m_signature = ss.str();
|
||||
return m_signature;
|
||||
}
|
||||
};
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
struct module_function
|
||||
{
|
||||
using width_t = std::array<size_t, 3>;
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
static constexpr size_t absolute_max_width = 80;
|
||||
|
||||
static auto& get_width()
|
||||
{
|
||||
static width_t _instance = []() {
|
||||
width_t _tmp;
|
||||
_tmp.fill(0);
|
||||
return _tmp;
|
||||
}();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
TIMEMORY_DEFAULT_OBJECT(module_function)
|
||||
|
||||
static void reset_width() { get_width().fill(0); }
|
||||
|
||||
static void update_width(const module_function& rhs)
|
||||
{
|
||||
get_width()[0] = std::max<size_t>(get_width()[0], rhs.module.length());
|
||||
get_width()[1] = std::max<size_t>(get_width()[1], rhs.function.length());
|
||||
get_width()[2] = std::max<size_t>(get_width()[2], rhs.signature.get().length());
|
||||
}
|
||||
|
||||
module_function(string_t _module, string_t _func, function_signature _sign,
|
||||
procedure_t* proc)
|
||||
: module(std::move(_module))
|
||||
, function(std::move(_func))
|
||||
, signature(std::move(_sign))
|
||||
{
|
||||
if(proc)
|
||||
{
|
||||
std::pair<address_t, address_t> _range{};
|
||||
if(proc->getAddressRange(_range.first, _range.second))
|
||||
address_range = _range.second - _range.first;
|
||||
}
|
||||
}
|
||||
|
||||
module_function(module_t* mod, procedure_t* proc)
|
||||
{
|
||||
char modname[FUNCNAMELEN];
|
||||
char fname[FUNCNAMELEN];
|
||||
|
||||
mod->getFullName(modname, FUNCNAMELEN);
|
||||
proc->getName(fname, FUNCNAMELEN);
|
||||
|
||||
module = modname;
|
||||
function = fname;
|
||||
signature = get_func_file_line_info(mod, proc);
|
||||
if(!proc->isInstrumentable())
|
||||
{
|
||||
verbprintf(0,
|
||||
"Warning! module function generated for un-instrumentable "
|
||||
"function: %s [%s]\n",
|
||||
function.c_str(), module.c_str());
|
||||
}
|
||||
std::pair<address_t, address_t> _range{};
|
||||
if(proc->getAddressRange(_range.first, _range.second))
|
||||
address_range = _range.second - _range.first;
|
||||
}
|
||||
|
||||
friend bool operator<(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return (lhs.module == rhs.module)
|
||||
? ((lhs.function == rhs.function)
|
||||
? (lhs.signature.get() < rhs.signature.get())
|
||||
: (lhs.function < rhs.function))
|
||||
: (lhs.module < rhs.module);
|
||||
}
|
||||
|
||||
friend bool operator==(const module_function& lhs, const module_function& rhs)
|
||||
{
|
||||
return std::tie(lhs.module, lhs.function, lhs.signature, lhs.address_range) ==
|
||||
std::tie(rhs.module, rhs.function, rhs.signature, rhs.address_range);
|
||||
}
|
||||
|
||||
static void write_header(std::ostream& os)
|
||||
{
|
||||
auto w0 = std::min<size_t>(get_width()[0], absolute_max_width);
|
||||
auto w1 = std::min<size_t>(get_width()[1], absolute_max_width);
|
||||
auto w2 = std::min<size_t>(get_width()[2], absolute_max_width);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::setw(14) << "AddressRange"
|
||||
<< " " << std::setw(w0 + 8) << std::left << "Module"
|
||||
<< " " << std::setw(w1 + 8) << std::left << "Function"
|
||||
<< " " << std::setw(w2 + 8) << std::left << "FunctionSignature"
|
||||
<< "\n";
|
||||
os << ss.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const module_function& rhs)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
auto w0 = std::min<size_t>(get_width()[0], absolute_max_width);
|
||||
auto w1 = std::min<size_t>(get_width()[1], absolute_max_width);
|
||||
auto w2 = std::min<size_t>(get_width()[2], absolute_max_width);
|
||||
|
||||
auto _get_str = [](const std::string& _inc) {
|
||||
if(_inc.length() > absolute_max_width)
|
||||
return _inc.substr(0, absolute_max_width - 3) + "...";
|
||||
return _inc;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
ss << std::setw(14) << rhs.address_range << " "
|
||||
<< std::setw(w0 + 8) << std::left << _get_str(rhs.module) << " "
|
||||
<< std::setw(w1 + 8) << std::left << _get_str(rhs.function) << " "
|
||||
<< std::setw(w2 + 8) << std::left << _get_str(rhs.signature.get());
|
||||
// clang-format on
|
||||
|
||||
os << ss.str();
|
||||
return os;
|
||||
}
|
||||
|
||||
size_t address_range = 0;
|
||||
string_t module = {};
|
||||
string_t function = {};
|
||||
function_signature signature = {};
|
||||
|
||||
template <typename ArchiveT>
|
||||
void serialize(ArchiveT& _ar, const unsigned)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
_ar(cereal::make_nvp("address_range", address_range),
|
||||
cereal::make_nvp("module", module), cereal::make_nvp("function", function),
|
||||
cereal::make_nvp("signature", signature));
|
||||
}
|
||||
};
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
static inline void
|
||||
dump_info(std::ostream& _os, const fmodset_t& _data)
|
||||
{
|
||||
module_function::reset_width();
|
||||
for(const auto& itr : _data)
|
||||
module_function::update_width(itr);
|
||||
|
||||
module_function::write_header(_os);
|
||||
for(const auto& itr : _data)
|
||||
_os << itr << '\n';
|
||||
|
||||
module_function::reset_width();
|
||||
}
|
||||
//
|
||||
template <typename ArchiveT,
|
||||
std::enable_if_t<tim::concepts::is_archive<ArchiveT>::value, int> = 0>
|
||||
static inline void
|
||||
dump_info(ArchiveT& _ar, const fmodset_t& _data)
|
||||
{
|
||||
_ar(tim::cereal::make_nvp("module_functions", _data));
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _label, string_t _oname, const string_t& _ext,
|
||||
const fmodset_t& _data, int _level, bool _fail)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
_oname += "." + _ext;
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[dump_info] Error opening '" << _oname << " for output";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
if(_fail)
|
||||
throw std::runtime_error(std::string{ "[omnitrace][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(!debug_print && verbose_level < _level) return;
|
||||
|
||||
if(_ext == "txt")
|
||||
{
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
dump_info(ofs, _data);
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "xml")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::XMLOutputArchive>;
|
||||
output_policy::indent() = true;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
std::stringstream oss{};
|
||||
{
|
||||
using output_policy = policy::output_archive<cereal::PrettyJSONOutputArchive>;
|
||||
auto ar = output_policy::get(oss);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
|
||||
std::ofstream ofs{};
|
||||
if(!tim::filepath::open(ofs, _oname))
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
verbprintf(_level, "Outputting '%s'... ", _oname.c_str());
|
||||
ofs << oss.str() << std::endl;
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
}
|
||||
ofs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[omnitrace][exe] Error in ", __FUNCTION__, " :: filename '", _oname,
|
||||
"' does not have one of recognized file extensions: txt, json, xml"));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
dump_info(const string_t& _oname, const fmodset_t& _data, int _level, bool _fail,
|
||||
const string_t& _type, const strset_t& _ext)
|
||||
{
|
||||
for(const auto& itr : _ext)
|
||||
dump_info(_type, _oname, itr, _data, _level, _fail);
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _label, const string_t& _iname, fmodset_t& _data, int _level)
|
||||
{
|
||||
namespace cereal = tim::cereal;
|
||||
namespace policy = tim::policy;
|
||||
|
||||
auto _pos = _iname.find_last_of('.');
|
||||
std::string _ext = {};
|
||||
if(_pos != std::string::npos) _ext = _iname.substr(_pos + 1, _iname.length());
|
||||
|
||||
auto _handle_error = [&]() {
|
||||
std::stringstream _msg{};
|
||||
_msg << "[load_info] Error opening '" << _iname << " for input";
|
||||
verbprintf(_level, "%s\n", _msg.str().c_str());
|
||||
throw std::runtime_error(std::string{ "[omnitrace][exe]" } + _msg.str());
|
||||
};
|
||||
|
||||
if(_ext == "xml")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::XMLInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else if(_ext == "json")
|
||||
{
|
||||
verbprintf(_level, "Reading '%s'... ", _iname.c_str());
|
||||
std::ifstream ifs{ _iname };
|
||||
if(!ifs)
|
||||
_handle_error();
|
||||
else
|
||||
{
|
||||
using input_policy = policy::input_archive<cereal::JSONInputArchive>;
|
||||
auto ar = input_policy::get(ifs);
|
||||
|
||||
ar->setNextName("omnitrace");
|
||||
ar->startNode();
|
||||
ar->setNextName(_label.c_str());
|
||||
ar->startNode();
|
||||
(*ar)(cereal::make_nvp("module_functions", _data));
|
||||
ar->finishNode();
|
||||
ar->finishNode();
|
||||
}
|
||||
verbprintf_bare(_level, "Done\n");
|
||||
ifs.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(TIMEMORY_JOIN(
|
||||
"", "[omnitrace][exe] Error in ", __FUNCTION__, " :: filename '", _iname,
|
||||
"' does not have one of recognized extentions: txt, json, xml :: ", _ext));
|
||||
}
|
||||
}
|
||||
//
|
||||
static inline void
|
||||
load_info(const string_t& _inp, std::map<std::string, fmodset_t*>& _data, int _level)
|
||||
{
|
||||
std::vector<std::string> _exceptions{};
|
||||
_exceptions.reserve(_data.size());
|
||||
for(auto& itr : _data)
|
||||
{
|
||||
try
|
||||
{
|
||||
fmodset_t _tmp{};
|
||||
load_info(itr.first, _inp, _tmp, _level);
|
||||
// add to the existing
|
||||
itr.second->insert(_tmp.begin(), _tmp.end());
|
||||
// if it did not throw it was successfully loaded
|
||||
_exceptions.clear();
|
||||
break;
|
||||
} catch(std::exception& _e)
|
||||
{
|
||||
_exceptions.emplace_back(_e.what());
|
||||
}
|
||||
}
|
||||
if(!_exceptions.empty())
|
||||
{
|
||||
std::stringstream _msg{};
|
||||
for(auto& itr : _exceptions)
|
||||
{
|
||||
_msg << "[omnitrace][exe] " << itr << "\n";
|
||||
}
|
||||
throw std::runtime_error(_msg.str());
|
||||
}
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
//
|
||||
template <typename Tp, std::enable_if_t<!std::is_same<Tp, std::string>::value, int> = 0>
|
||||
snippet_pointer_t
|
||||
get_snippet(Tp arg)
|
||||
@@ -962,4 +282,79 @@ omnitrace_fork_callback(thread_t* parent, thread_t* child)
|
||||
}
|
||||
//
|
||||
//======================================================================================//
|
||||
// insert_instr -- generic insert instrumentation function
|
||||
//
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, flow_graph_t* cfGraph,
|
||||
basic_loop_t* loopToInstrument, bool allow_traps)
|
||||
{
|
||||
module_t* module = funcToInstr->getModule();
|
||||
if(!module || !traceFunc) return false;
|
||||
|
||||
bpvector_t<point_t*>* _points = nullptr;
|
||||
auto _trace = traceFunc.get();
|
||||
|
||||
if(cfGraph && loopToInstrument)
|
||||
{
|
||||
if(traceLoc == BPatch_entry)
|
||||
_points = cfGraph->findLoopInstPoints(BPatch_locLoopEntry, loopToInstrument);
|
||||
else if(traceLoc == BPatch_exit)
|
||||
_points = cfGraph->findLoopInstPoints(BPatch_locLoopExit, loopToInstrument);
|
||||
}
|
||||
else
|
||||
{
|
||||
_points = funcToInstr->findPoint(traceLoc);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for(auto& itr : *_points)
|
||||
{
|
||||
if(itr && itr->usesTrap_NP()) _traps.insert(itr);
|
||||
}
|
||||
}
|
||||
|
||||
size_t _n = 0;
|
||||
for(auto& itr : *_points)
|
||||
{
|
||||
if(!itr || _traps.count(itr) > 0)
|
||||
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;
|
||||
}
|
||||
|
||||
return (_n > 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
# adds a ctest for executable
|
||||
function(OMNITRACE_ADD_BIN_TEST)
|
||||
cmake_parse_arguments(
|
||||
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
|
||||
# value args
|
||||
${ARGN})
|
||||
|
||||
if(NOT OMNITRACE_DYNINST_API_RT_DIR AND OMNITRACE_DYNINST_API_RT)
|
||||
get_filename_component(OMNITRACE_DYNINST_API_RT_DIR "${OMNITRACE_DYNINST_API_RT}"
|
||||
DIRECTORY)
|
||||
endif()
|
||||
|
||||
if(OMNITRACE_BUILD_DYNINST)
|
||||
set(OMNITRACE_DYNINST_API_RT_DIR
|
||||
"${PROJECT_BINARY_DIR}/external/dyninst/dyninstAPI_RT:${PROJECT_BINARY_DIR}/external/dyninst/dyninstAPI"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT TEST_ENVIRONMENT)
|
||||
set(TEST_ENVIRONMENT
|
||||
"OMNITRACE_USE_PERFETTO=ON"
|
||||
"OMNITRACE_USE_TIMEMORY=ON"
|
||||
"OMNITRACE_USE_SAMPLING=ON"
|
||||
"OMNITRACE_TIME_OUTPUT=OFF"
|
||||
"LD_LIBRARY_PATH=${PROJECT_BINARY_DIR}:${OMNITRACE_DYNINST_API_RT_DIR}:$ENV{LD_LIBRARY_PATH}"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND TEST_ENVIRONMENT "OMNITRACE_CI=ON"
|
||||
"OMNITRACE_OUTPUT_PATH=omnitrace-tests-output"
|
||||
"OMNITRACE_OUTPUT_PREFIX=${TEST_NAME}/")
|
||||
|
||||
if(TEST_COMMAND)
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND ${TEST_COMMAND} ${TEST_ARGS}
|
||||
WORKING_DIRECTORY ${TEST_WORKING_DIRECTORY})
|
||||
|
||||
set_tests_properties(
|
||||
${TEST_NAME}
|
||||
PROPERTIES ENVIRONMENT
|
||||
"${TEST_ENVIRONMENT}"
|
||||
TIMEOUT
|
||||
${TEST_TIMEOUT}
|
||||
LABELS
|
||||
"omnitrace-bin;${TEST_LABELS}"
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"${TEST_PASS_REGULAR_EXPRESSION}"
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${TEST_FAIL_REGULAR_EXPRESSION}"
|
||||
SKIP_REGULAR_EXPRESSION
|
||||
"${TEST_SKIP_REGULAR_EXPRESSION}"
|
||||
${TEST_PROPERTIES})
|
||||
elseif(TARGET ${TEST_TARGET})
|
||||
add_test(
|
||||
NAME ${TEST_NAME}
|
||||
COMMAND $<TARGET_FILE:${TEST_TARGET}> ${TEST_ARGS}
|
||||
WORKING_DIRECTORY $<TARGET_FILE_DIR:${TEST_TARGET}>)
|
||||
|
||||
set_tests_properties(
|
||||
${TEST_NAME}
|
||||
PROPERTIES ENVIRONMENT
|
||||
"${TEST_ENVIRONMENT}"
|
||||
TIMEOUT
|
||||
${TEST_TIMEOUT}
|
||||
LABELS
|
||||
"omnitrace-bin;${TEST_LABELS}"
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"${TEST_PASS_REGULAR_EXPRESSION}"
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${TEST_FAIL_REGULAR_EXPRESSION}"
|
||||
SKIP_REGULAR_EXPRESSION
|
||||
"${TEST_SKIP_REGULAR_EXPRESSION}"
|
||||
${TEST_PROPERTIES})
|
||||
elseif(OMNITRACE_BUILD_TESTING)
|
||||
message(FATAL_ERROR "Error! ${TEST_TARGET} does not exist")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-exe-help
|
||||
TARGET omnitrace-exe
|
||||
ARGS --help
|
||||
LABELS omnitrace-exe
|
||||
TIMEOUT 15
|
||||
PASS_REGULAR_EXPRESSION
|
||||
".*\\\[omnitrace\\\] Usage:.*\\\[DEBUG OPTIONS\\\].*\\\[MODE OPTIONS\\\].*\\\[LIBRARY OPTIONS\\\].*\\\[SYMBOL SELECTION OPTIONS\\\].*\\\[RUNTIME OPTIONS\\\].*\\\[GRANULARITY OPTIONS\\\].*\\\[DYNINST OPTIONS\\\].*"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-exe-simulate-ls
|
||||
TARGET omnitrace-exe
|
||||
ARGS --simulate --print-format json txt xml -- ls
|
||||
TIMEOUT 60)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-exe-simulate-ls-check
|
||||
DEPENDS omnitrace-exe-simulate-ls
|
||||
COMMAND ls
|
||||
WORKING_DIRECTORY
|
||||
${PROJECT_BINARY_DIR}/omnitrace-tests-output/omnitrace-exe-simulate-ls
|
||||
TIMEOUT 30
|
||||
PASS_REGULAR_EXPRESSION
|
||||
".*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.*"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-avail-help
|
||||
TARGET omnitrace-avail
|
||||
ARGS --help
|
||||
LABELS omnitrace-avail
|
||||
TIMEOUT 15
|
||||
PASS_REGULAR_EXPRESSION
|
||||
".*\\\[omnitrace-avail\\\] Usage:.*\\\[CATEGORIES\\\].*\\\[VIEW OPTIONS\\\].*\\\[COLUMN OPTIONS\\\].*\\\[WIDTH OPTIONS\\\].*\\\[OUTPUT OPTIONS\\\].*"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-avail-filter-wall-clock-available
|
||||
TARGET omnitrace-avail
|
||||
ARGS -r wall_clock -C --available
|
||||
LABELS omnitrace-avail
|
||||
TIMEOUT 15
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"\\\|[-]+\\\|\n\\\|[ ]+COMPONENT[ ]+\\\|\n\\\|[-]+\\\|\n\\\| (wall_clock)[ ]+\\\|\n\\\| (sampling_wall_clock)[ ]+\\\|\n\\\|[-]+\\\|"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-avail-category-filer-omnitrace
|
||||
TARGET omnitrace-avail
|
||||
ARGS --categories settings::omnitrace --brief
|
||||
LABELS omnitrace-avail
|
||||
TIMEOUT 15
|
||||
PASS_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)"
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-avail-category-filer-timemory
|
||||
TARGET omnitrace-avail
|
||||
ARGS --categories settings::timemory --brief
|
||||
LABELS omnitrace-avail
|
||||
TIMEOUT 15
|
||||
PASS_REGULAR_EXPRESSION
|
||||
"OMNITRACE_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
|
||||
FAIL_REGULAR_EXPRESSION "OMNITRACE_(SETTINGS_DESC|OUTPUT_FILE)")
|
||||
Reference in New Issue
Block a user