Critical trace updates (#24)

* Source code restructuring

* Critical trace updates following restructuring

* thread_sampler, timestamps

- thread_sampler
- CPU frequency managed via thread_sampler
- rocm-smi managed via thread_sampler
- Use consistent timestamps for perfetto
- removed hsa_timer_t in favor of wall_clock::record()
- disable KokkosP by default
- re-enable critical-trace testing

* cmake-format

* Fix for defines.hpp.in

* Remove OMNITRACE_ROCM_SMI_FREQ

- thread_sampler freq is set via OMNITRACE_SAMPLING_FREQ w/ max of 1000

* Increase CI Install Dyninst timeout

* Debug macros + omnitrace_init_tooling + config

- new debug macros
- extern "C" omnitrace_init_tooling
- guard get_rocm_smi_devices

* Miscellaneous tweaks

- tweak to transpose
- critical_trace::Device::ANY
- perfetto "critical-trace" category
- OMNITRACE_VERBOSE usage

* Disable key and tid data for HIP API calls

- non-kernels are ignored in activity callback

* critical-trace exe updates

- fix perfetto generation
- improved logging
- improved readability

* timemory submodule update

- lulesh example cmake tweaks
Cette révision appartient à :
Jonathan R. Madsen
2022-02-19 02:00:59 -06:00
révisé par GitHub
Parent 39f17ae8b8
révision b016c8929f
70 fichiers modifiés avec 2369 ajouts et 1434 suppressions
+3
Voir le fichier
@@ -0,0 +1,3 @@
add_subdirectory(omnitrace-avail)
add_subdirectory(omnitrace-critical-trace)
add_subdirectory(omnitrace)
+20
Voir le fichier
@@ -0,0 +1,20 @@
# ------------------------------------------------------------------------------#
#
# omnitrace-avail target
#
# ------------------------------------------------------------------------------#
add_executable(
omnitrace-avail
${CMAKE_CURRENT_LIST_DIR}/avail.cpp ${CMAKE_CURRENT_LIST_DIR}/avail.hpp
$<TARGET_OBJECTS:omnitrace::omnitrace-object-library>)
target_include_directories(omnitrace-avail PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include)
target_compile_definitions(omnitrace-avail PRIVATE OMNITRACE_EXTERN_COMPONENTS=0)
target_link_libraries(omnitrace-avail PRIVATE omnitrace::omnitrace-interface-library)
set_target_properties(omnitrace-avail PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
install(
TARGETS omnitrace-avail
DESTINATION bin
OPTIONAL)
Fichier diff supprimé car celui-ci est trop grand Voir la Diff
+338
Voir le fichier
@@ -0,0 +1,338 @@
// MIT License
//
// Copyright (c) 2020, The Regents of the University of California,
// through Lawrence Berkeley National Laboratory (subject to receipt of any
// required approvals from the U.S. Dept. of Energy). 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
// 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.
/** \file timemory/tools/available.hpp
* \headerfile tools/available.hpp "tools/available.hpp"
* Handles serializing the settings
*
*/
#pragma once
#define TIMEMORY_DISABLE_BANNER
#define TIMEMORY_DISABLE_COMPONENT_STORAGE_INIT
#include <timemory/settings/macros.hpp>
#include <timemory/tpls/cereal/archives.hpp>
#include <timemory/tpls/cereal/cereal/external/base64.hpp>
#include <timemory/utility/demangle.hpp>
#include <algorithm>
#include <array>
#include <functional>
#include <iomanip>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#if !defined(TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME)
# define TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME epilogue
#endif
#if !defined(TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME)
# define TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME prologue
#endif
//======================================================================================//
namespace tim
{
namespace cereal
{
class SettingsTextArchive
: public OutputArchive<SettingsTextArchive>
, public traits::TextArchive
{
public:
using width_type = std::vector<uint64_t>;
using value_type = std::string;
using entry_type = std::map<std::string, value_type>;
using array_type = std::vector<entry_type>;
using unique_set = std::set<std::string>;
using int_stack = std::stack<uint32_t>;
public:
//! Construct, outputting to the provided stream
/// \param stream The array of output data
SettingsTextArchive(array_type& stream, unique_set exclude)
: OutputArchive<SettingsTextArchive>(this)
, output_stream(&stream)
, exclude_stream(std::move(exclude))
{
name_counter.push(0);
}
~SettingsTextArchive() override = default;
void saveBinaryValue(const void* data, size_t size, const char* name = nullptr)
{
setNextName(name);
writeName();
auto base64string =
base64::encode(reinterpret_cast<const unsigned char*>(data), size);
saveValue(base64string);
}
void startNode() { name_counter.push(0); }
void finishNode() { name_counter.pop(); }
//! Sets the name for the next node created with startNode
void setNextName(const char* name)
{
if(exclude_stream.count(name) > 0) return;
if((current_entry != nullptr) && value_keys.count(name) > 0)
{
current_entry->insert({ name, "" });
current_value = &((*current_entry)[name]);
return;
}
if(value_keys.count(name) > 0)
{
return;
}
current_value = nullptr;
output_stream->push_back(entry_type{});
current_entry = &(output_stream->back());
current_entry->insert({ "identifier", name });
std::string func = name;
const std::string prefix = TIMEMORY_SETTINGS_PREFIX;
func = func.erase(0, prefix.length());
std::transform(func.begin(), func.end(), func.begin(),
[](char& c) { return tolower(c); });
{
std::stringstream ss;
ss << "settings::" << func << "()";
current_entry->insert({ "static_accessor", ss.str() });
}
{
std::stringstream ss;
ss << "settings::instance()->get_" << func << "()";
current_entry->insert({ "member_accessor", ss.str() });
}
{
std::stringstream ss;
ss << "settings." << func;
current_entry->insert({ "python_accessor", ss.str() });
}
}
void setNextType(const char*) {}
public:
template <typename Tp>
inline void saveValue(Tp _val)
{
std::stringstream ssval;
ssval << std::boolalpha << _val;
if(current_value)
{
*current_value = ssval.str();
}
}
void writeName() {}
void makeArray() {}
private:
value_type* current_value = nullptr;
entry_type* current_entry = nullptr;
array_type* output_stream = nullptr;
unique_set exclude_stream = {};
int_stack name_counter;
unique_set value_keys = { "name", "value", "description", "count",
"environ", "max_count", "cmdline", "data_type",
"initial", "categories" };
};
//======================================================================================//
//
// prologue and epilogue functions
//
//======================================================================================//
//--------------------------------------------------------------------------------------//
//! Prologue for NVPs for settings archive
/*! NVPs do not start or finish nodes - they just set up the names */
template <typename T>
inline void
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const NameValuePair<T>&)
{}
//--------------------------------------------------------------------------------------//
//! Epilogue for NVPs for settings archive
/*! NVPs do not start or finish nodes - they just set up the names */
template <typename T>
inline void
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const NameValuePair<T>&)
{}
//--------------------------------------------------------------------------------------//
//! Prologue for deferred data for settings archive
/*! Do nothing for the defer wrapper */
template <typename T>
inline void
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const DeferredData<T>&)
{}
//--------------------------------------------------------------------------------------//
//! Epilogue for deferred for settings archive
/*! NVPs do not start or finish nodes - they just set up the names */
template <typename T>
inline void
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const DeferredData<T>&)
{}
//--------------------------------------------------------------------------------------//
//! Prologue for SizeTags for settings archive
/*! SizeTags are ignored */
template <typename T>
inline void
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const SizeTag<T>&)
{
ar.makeArray();
}
//--------------------------------------------------------------------------------------//
//! Epilogue for SizeTags for settings archive
/*! SizeTags are ignored */
template <typename T>
inline void
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const SizeTag<T>&)
{}
//--------------------------------------------------------------------------------------//
//! Prologue for all other types for settings archive
/*! Starts a new node, named either automatically or by some NVP,
that may be given data by the type about to be archived*/
template <typename T>
inline void
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const T&)
{
ar.startNode();
}
//--------------------------------------------------------------------------------------//
//! Epilogue for all other types other for settings archive
/*! Finishes the node created in the prologue*/
template <typename T>
inline void
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive& ar, const T&)
{
ar.finishNode();
}
//--------------------------------------------------------------------------------------//
//! Prologue for arithmetic types for settings archive
inline void
TIMEMORY_CEREAL_PROLOGUE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
{}
//--------------------------------------------------------------------------------------//
//! Epilogue for arithmetic types for settings archive
inline void
TIMEMORY_CEREAL_EPILOGUE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
{}
//======================================================================================//
//
// Common serialization functions
//
//======================================================================================//
//! Serializing NVP types
template <typename T>
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar, const NameValuePair<T>& t)
{
ar.setNextName(t.name);
if(std::is_same<T, std::string>::value)
{
ar.setNextType("string");
}
else
{
ar.setNextType(tim::demangle<T>().c_str());
}
ar(t.value);
}
template <typename CharT, typename Traits, typename Alloc>
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(
SettingsTextArchive& ar,
const NameValuePair<std::basic_string<CharT, Traits, Alloc>>& t)
{
ar.setNextName(t.name);
ar.setNextType("string");
ar(t.value);
}
//! Saving for nullptr
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive&, const std::nullptr_t&)
{}
//! Saving for arithmetic
template <typename T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar, const T& t)
{
if(std::is_same<T, std::string>::value) ar.setNextType("string");
ar.saveValue(t);
}
//! saving string
template <typename CharT, typename Traits, typename Alloc>
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive& ar,
const std::basic_string<CharT, Traits, Alloc>& str)
{
ar.setNextType("string");
ar.saveValue(str);
}
//--------------------------------------------------------------------------------------//
//! Saving SizeTags
template <typename T>
inline void
TIMEMORY_CEREAL_SAVE_FUNCTION_NAME(SettingsTextArchive&, const SizeTag<T>&)
{
// nothing to do here, we don't explicitly save the size
}
} // namespace cereal
} // namespace tim
// register archives for polymorphic support
TIMEMORY_CEREAL_REGISTER_ARCHIVE(SettingsTextArchive)
+23
Voir le fichier
@@ -0,0 +1,23 @@
# ------------------------------------------------------------------------------#
#
# omnitrace-critical-trace target
#
# ------------------------------------------------------------------------------#
add_executable(
omnitrace-critical-trace
${CMAKE_CURRENT_LIST_DIR}/critical-trace.cpp
${CMAKE_CURRENT_LIST_DIR}/critical-trace.hpp
$<TARGET_OBJECTS:omnitrace::omnitrace-object-library>)
target_include_directories(omnitrace-critical-trace
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include)
target_compile_definitions(omnitrace-critical-trace PRIVATE OMNITRACE_EXTERN_COMPONENTS=0)
target_link_libraries(omnitrace-critical-trace
PRIVATE omnitrace::omnitrace-interface-library)
set_target_properties(omnitrace-critical-trace PROPERTIES INSTALL_RPATH_USE_LINK_PATH ON)
install(
TARGETS omnitrace-critical-trace
DESTINATION bin
OPTIONAL)
+952
Voir le fichier
@@ -0,0 +1,952 @@
// 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 "critical-trace.hpp"
#include "library/api.hpp"
#include "library/perfetto.hpp"
#include <timemory/hash/types.hpp>
#include <cstdlib>
namespace config = omnitrace::config;
namespace critical_trace = omnitrace::critical_trace;
int
main(int argc, char** argv)
{
omnitrace_init_library();
config::set_setting_value("OMNITRACE_CRITICAL_TRACE", true);
// config::set_setting_value("OMNITRACE_CRITICAL_TRACE_DEBUG", true);
config::set_setting_value<int64_t>("OMNITRACE_CRITICAL_TRACE_COUNT", 500);
config::set_setting_value<int64_t>("OMNITRACE_CRITICAL_TRACE_PER_ROW", 100);
config::set_setting_value<int64_t>("OMNITRACE_CRITICAL_TRACE_NUM_THREADS",
std::thread::hardware_concurrency());
config::set_setting_value("OMNITRACE_CRITICAL_TRACE_SERIALIZE_NAMES", true);
perfetto::TracingInitArgs args{};
perfetto::TraceConfig cfg{};
perfetto::protos::gen::TrackEventConfig track_event_cfg{};
auto shmem_size_hint = config::get_perfetto_shmem_size_hint();
auto buffer_size = config::get_perfetto_buffer_size();
auto* buffer_config = cfg.add_buffers();
buffer_config->set_size_kb(buffer_size);
buffer_config->set_fill_policy(
perfetto::protos::gen::TraceConfig_BufferConfig_FillPolicy_DISCARD);
auto* ds_cfg = cfg.add_data_sources()->mutable_config();
ds_cfg->set_name("track_event");
ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
args.backends |= perfetto::kInProcessBackend;
args.shmem_size_hint_kb = shmem_size_hint;
perfetto::Tracing::Initialize(args);
perfetto::TrackEvent::Register();
auto tracing_session = perfetto::Tracing::NewTrace();
tracing_session->Setup(cfg);
tracing_session->StartBlocking();
for(int i = 1; i < argc; ++i)
{
critical_trace::complete_call_chain = {};
OMNITRACE_BASIC_PRINT_F("Loading call-chain %s...\n", argv[i]);
critical_trace::load_call_chain(argv[i], "call_chain",
critical_trace::complete_call_chain);
for(const auto& itr : *tim::get_hash_ids())
critical_trace::complete_hash_ids.emplace(itr.second);
OMNITRACE_BASIC_PRINT_F("Computing critical trace for %s...\n", argv[i]);
critical_trace::compute_critical_trace();
}
// Make sure the last event is closed for this example.
perfetto::TrackEvent::Flush();
OMNITRACE_DEBUG_F("Stopping the blocking perfetto trace sessions...\n");
tracing_session->StopBlocking();
OMNITRACE_DEBUG_F("Getting the trace data...\n");
std::vector<char> trace_data{ tracing_session->ReadTraceBlocking() };
if(trace_data.empty())
{
OMNITRACE_BASIC_PRINT_F(
"> trace data is empty. File '%s' will not be written...\n",
config::get_perfetto_output_filename().c_str());
}
else
{
// Write the trace into a file.
OMNITRACE_CONDITIONAL_BASIC_PRINT_F(
config::get_verbose() >= 0,
"> Outputting '%s' (%.2f KB / %.2f MB / %.2f GB)... ",
config::get_perfetto_output_filename().c_str(),
static_cast<double>(trace_data.size()) / tim::units::KB,
static_cast<double>(trace_data.size()) / tim::units::MB,
static_cast<double>(trace_data.size()) / tim::units::GB);
std::ofstream ofs{};
if(!tim::filepath::open(ofs, config::get_perfetto_output_filename(),
std::ios::out | std::ios::binary))
{
OMNITRACE_BASIC_PRINT_F("> Error opening '%s'...\n",
config::get_perfetto_output_filename().c_str());
return EXIT_FAILURE;
}
else
{
// Write the trace into a file.
if(config::get_verbose() >= 0) fprintf(stderr, "Done\n");
ofs.write(&trace_data[0], trace_data.size());
}
ofs.close();
}
}
namespace omnitrace
{
namespace critical_trace
{
namespace
{
//--------------------------------------------------------------------------------------//
std::string
get_perf_name(std::string _func)
{
const auto _npos = std::string::npos;
auto _pos = std::string::npos;
while((_pos = _func.find('_')) != _npos)
_func = _func.replace(_pos, 1, " ");
if(_func.length() > 0) _func.at(0) = std::toupper(_func.at(0));
return _func;
}
//--------------------------------------------------------------------------------------//
void
save_call_graph(const std::string& _fname, const std::string& _label,
const call_graph_t& _call_graph, bool _msg = false,
std::string _func = {})
{
OMNITRACE_CT_DEBUG("\n");
using perfstats_t =
tim::lightweight_tuple<comp::wall_clock, comp::peak_rss, comp::page_rss>;
perfstats_t _perf{ get_perf_name(__FUNCTION__) };
_perf.start();
std::stringstream oss{};
{
namespace cereal = tim::cereal;
auto ar = tim::policy::output_archive<cereal::MinimalJSONOutputArchive>::get(oss);
auto _hash_map = *tim::hash::get_hash_ids();
for(auto& itr : _hash_map)
itr.second = tim::demangle(itr.second);
ar->setNextName("omnitrace");
ar->startNode();
(*ar)(cereal::make_nvp("hash_map", _hash_map));
ar->setNextName(_label.c_str());
ar->startNode();
serialize_graph(*ar, _call_graph);
ar->finishNode();
ar->finishNode();
}
std::ofstream ofs{};
if(tim::filepath::open(ofs, _fname))
{
if(_msg)
{
if(_func.empty()) _func = __FUNCTION__;
OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0,
"[%s] Outputting '%s'...\n", _func.c_str(),
_fname.c_str());
}
ofs << oss.str() << std::endl;
}
_perf.stop();
if(_msg)
{
OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str());
}
}
void
save_critical_trace(const std::string& _fname, const std::string& _label,
const std::vector<call_chain>& _cchain, bool _msg = false,
std::string _func = {})
{
OMNITRACE_CT_DEBUG("\n");
using perfstats_t =
tim::lightweight_tuple<comp::wall_clock, comp::peak_rss, comp::page_rss>;
perfstats_t _perf{ get_perf_name(__FUNCTION__) };
_perf.start();
auto _save = [&](std::ostream& _os) {
namespace cereal = tim::cereal;
auto ar = tim::policy::output_archive<cereal::MinimalJSONOutputArchive>::get(_os);
auto _hash_map = *tim::hash::get_hash_ids();
for(auto& itr : _hash_map)
itr.second = tim::demangle(itr.second);
ar->setNextName("omnitrace");
ar->startNode();
(*ar)(cereal::make_nvp("hash_map", _hash_map),
cereal::make_nvp(_label.c_str(), _cchain));
ar->finishNode();
};
std::ofstream ofs{};
if(tim::filepath::open(ofs, _fname))
{
if(_msg)
{
if(_func.empty()) _func = __FUNCTION__;
OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0,
"[%s] Outputting '%s'...\n", _func.c_str(),
_fname.c_str());
}
std::stringstream oss{};
if(_cchain.size() > 1000)
{
_save(ofs);
}
else
{
_save(oss);
ofs << oss.str() << std::endl;
}
}
_perf.stop();
if(_msg)
{
OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str());
}
}
void
save_call_chain_text(const std::string& _fname, const call_chain& _call_chain,
bool _msg = false, std::string _func = {})
{
OMNITRACE_CT_DEBUG("\n");
using perfstats_t =
tim::lightweight_tuple<comp::wall_clock, comp::peak_rss, comp::page_rss>;
perfstats_t _perf{ get_perf_name(__FUNCTION__) };
_perf.start();
std::ofstream ofs{};
if(tim::filepath::open(ofs, _fname))
{
if(_msg)
{
if(_func.empty()) _func = __FUNCTION__;
OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0,
"[%s] Outputting '%s'...\n", _func.c_str(),
_fname.c_str());
}
ofs << _call_chain << "\n";
}
_perf.stop();
if(_msg)
{
OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str());
}
}
void
save_call_chain_json(const std::string& _fname, const std::string& _label,
const call_chain& _call_chain, bool _msg = false,
std::string _func = {})
{
OMNITRACE_CT_DEBUG("\n");
using perfstats_t =
tim::lightweight_tuple<comp::wall_clock, comp::peak_rss, comp::page_rss>;
perfstats_t _perf{ get_perf_name(__FUNCTION__) };
_perf.start();
auto _save = [&](std::ostream& _os) {
namespace cereal = tim::cereal;
auto ar = tim::policy::output_archive<cereal::MinimalJSONOutputArchive>::get(_os);
auto _hash_map = *tim::hash::get_hash_ids();
for(auto& itr : _hash_map)
itr.second = tim::demangle(itr.second);
ar->setNextName("omnitrace");
ar->startNode();
(*ar)(cereal::make_nvp("hash_map", _hash_map),
cereal::make_nvp(_label.c_str(), _call_chain));
ar->finishNode();
};
std::ofstream ofs{};
if(tim::filepath::open(ofs, _fname))
{
if(_msg)
{
if(_func.empty()) _func = __FUNCTION__;
OMNITRACE_CONDITIONAL_BASIC_PRINT(get_verbose() >= 0,
"[%s] Outputting '%s'...\n", _func.c_str(),
_fname.c_str());
}
std::stringstream oss{};
if(_call_chain.size() > 100000)
{
_save(ofs);
}
else
{
_save(oss);
ofs << oss.str() << std::endl;
}
}
_perf.stop();
if(_msg)
{
OMNITRACE_CT_DEBUG("%s\n", JOIN("", _perf).substr(4).c_str());
}
}
void
load_call_chain(const std::string& _fname, const std::string& _label,
call_chain& _call_chain)
{
std::ifstream ifs{};
ifs.open(_fname);
if(ifs && ifs.is_open())
{
namespace cereal = tim::cereal;
auto ar = tim::policy::input_archive<cereal::JSONInputArchive>::get(ifs);
ar->setNextName("omnitrace");
ar->startNode();
(*ar)(cereal::make_nvp(_label.c_str(), _call_chain));
ar->finishNode();
}
}
auto
get_indexed(const call_chain& _chain)
{
OMNITRACE_CT_DEBUG("\n");
std::map<int64_t, std::vector<entry>> _indexed = {};
// allocate for all cpu correlation ids
for(const auto& itr : _chain)
{
_indexed.emplace(static_cast<int64_t>(itr.cpu_cid), std::vector<entry>{});
_indexed.emplace(static_cast<int64_t>(itr.parent_cid), std::vector<entry>{});
}
// index based on parent correlation id
for(const auto& itr : _chain)
{
if(itr.depth < 1 && itr.phase == Phase::BEGIN) continue;
_indexed[static_cast<int64_t>(itr.parent_cid)].emplace_back(itr);
}
for(auto& itr : _indexed)
std::sort(itr.second.begin(), itr.second.end(),
[](const entry& lhs, const entry& rhs) {
// return lhs.cpu_cid < rhs.cpu_cid;
return lhs.begin_ns < rhs.begin_ns;
});
return _indexed;
}
void
find_children(PTL::ThreadPool& _tp, call_graph_t& _graph, const call_chain& _chain)
{
OMNITRACE_CT_DEBUG("\n");
using iterator_t = call_graph_sibling_itr_t;
using itr_entry_vec_t = std::vector<std::pair<iterator_t, entry>>;
using task_group_t = PTL::TaskGroup<void>;
auto _indexed = get_indexed(_chain);
std::map<entry, std::vector<entry>> _entry_map{};
// allocate all entries
OMNITRACE_CT_DEBUG_F("Allocating...\n");
for(const auto& itr : _chain)
{
auto _ins = _entry_map.emplace(itr, std::vector<entry>{});
if(!_ins.second)
{
auto _existing = _ins.first->first;
OMNITRACE_BASIC_PRINT("Warning! Duplicate entry for [%s] :: [%s]\n",
JOIN("", _existing).c_str(), JOIN("", itr).c_str());
}
}
task_group_t _tg{ &_tp };
OMNITRACE_CT_DEBUG_F("Parallel mapping...\n");
for(const auto& itr : _chain)
{
_tg.run([&]() { _entry_map[itr] = _indexed.at(itr.cpu_cid); });
}
_tg.join();
std::function<void(iterator_t, const entry&)> _recursive_func;
_recursive_func = [&](iterator_t itr, const entry& _v) {
auto _child = _graph.append_child(itr, _v);
auto _children = std::move(_entry_map[_v]);
_entry_map[_v].clear();
for(auto&& vitr : _children)
{
_recursive_func(_child, vitr);
}
};
// the recursive version of _func + _loop_func has a tendency to overflow the stack
auto _func = [&](iterator_t itr, const entry& _v) {
auto _child = _graph.append_child(itr, _v);
auto _children = std::move(_entry_map[_v]);
_entry_map[_v].clear();
itr_entry_vec_t _data{};
for(auto&& vitr : _children)
_data.emplace_back(_child, vitr);
return _data;
};
auto _loop_func = [&_func](itr_entry_vec_t& _data) {
auto _inp = _data;
_data.clear();
for(auto itr : _inp)
{
for(auto&& fitr : _func(itr.first, itr.second))
_data.emplace_back(std::move(fitr));
}
// if data is empty return false so we can break out of while loop
return !_data.empty();
};
if(!_indexed.at(-1).empty())
{
OMNITRACE_CT_DEBUG_F("Setting root (line %i)...\n", __LINE__);
_graph.set_head(_indexed.at(-1).front());
}
else
{
OMNITRACE_CT_DEBUG_F("Setting root (line %i)...\n", __LINE__);
auto _depth = static_cast<uint16_t>(-1);
entry _root{ 0, Device::NONE, Phase::NONE, _depth, 0, 0, 0, 0, 0, 0, 0 };
_graph.set_head(_root);
}
iterator_t _root = _graph.begin();
for(auto&& itr : _entry_map)
{
if(itr.first.depth == _root->depth + 1)
{
OMNITRACE_CT_DEBUG_F("Generating call-graph...\n");
// _recursive_func(_root, itr.first);
itr_entry_vec_t _data = _func(_root, itr.first);
while(_loop_func(_data))
{}
}
}
}
void
find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph,
std::vector<call_chain>& _chain)
{
OMNITRACE_CT_DEBUG("\n");
/*
using sibling_itr_t = call_graph_sibling_itr_t;
using sibling_vec_t = std::vector<sibling_itr_t>;
using sibling_map_t = std::map<int64_t, sibling_vec_t>;
std::function<void(sibling_map_t & _v, sibling_itr_t root)> _no_overlap{};
_no_overlap = [&](sibling_map_t& _v, sibling_itr_t root) {
sibling_map_t _l{};
int64_t n = _graph.number_of_children(root);
if(n == 0) return;
//_graph.sort(sibling_itr_t{ root },
// [](auto lhs, auto rhs) { return lhs.get_cost() > rhs.get_cost(); });
for(int64_t i = 0; i < n; ++i)
{
if(_l.empty())
{
auto itr = _graph.child(root, i);
_l[itr->tid].emplace_back(itr);
}
else
{
auto itr = _graph.child(root, i);
bool _overlaps = false;
for(auto& litr : _l[itr->tid])
{
if(litr->device == itr->device && litr->get_overlap(*itr) > 0)
{
_overlaps = true;
break;
}
}
if(!_overlaps) _l[itr->tid].emplace_back(itr);
}
}
for(auto& iitr : _l)
{
for(auto itr : iitr.second)
{
_v[iitr.first].emplace_back(itr);
_no_overlap(_v, itr);
}
}
};
std::map<int64_t, sibling_vec_t> _tot{};
for(sibling_itr_t itr = _graph.begin(); itr != _graph.end(); ++itr)
{
_no_overlap(_tot, itr);
}
for(const auto& iitr : _tot)
{
call_chain _cc{};
_cc.emplace_back(*_graph.begin());
for(const auto& itr : iitr.second)
_cc.emplace_back(*itr);
_chain.emplace_back(_cc);
}
(void) _tp;
*/
using iterator_t = call_graph_preorder_itr_t;
std::vector<iterator_t> _end_nodes{};
size_t _n = 0;
for(iterator_t itr = _graph.begin(); itr != _graph.end(); ++itr, ++_n)
{
auto _nchild = _graph.number_of_children(itr);
if(_nchild > 0)
{
OMNITRACE_CT_DEBUG("Skipping node #%zu with %u children :: %s\n", _n, _nchild,
JOIN("", *itr).c_str());
continue;
}
_end_nodes.emplace_back(itr);
}
OMNITRACE_CT_DEBUG("Number of end nodes: %zu\n", _end_nodes.size());
_chain.resize(_end_nodes.size());
auto _construct = [&](size_t i) {
auto itr = _end_nodes.at(i);
while(itr != nullptr && _graph.is_valid(itr))
{
_chain.at(i).emplace_back(*itr);
itr = _graph.parent(itr);
}
std::reverse(_chain.at(i).begin(), _chain.at(i).end());
std::sort(
_chain.at(i).begin(), _chain.at(i).end(),
[](const entry& lhs, const entry& rhs) { return lhs.begin_ns > rhs.end_ns; });
};
PTL::TaskGroup<void> _tg{ &_tp };
for(size_t i = 0; i < _end_nodes.size(); ++i)
_tg.run(_construct, i);
_tg.join();
std::sort(_chain.begin(), _chain.end(),
[](const call_chain& lhs, const call_chain& rhs) {
return lhs.get_cost() > rhs.get_cost();
});
/*
std::vector<call_chain> _new_chain{};
for(auto& itr : _chain)
{
if(itr.empty()) continue;
if(_new_chain.empty())
{
_new_chain.emplace_back(std::move(itr));
continue;
}
std::sort(itr.begin(), itr.end(), [](const entry& lhs, const entry& rhs) {
return lhs.get_cost() > rhs.get_cost();
});
call_chain* _append_chain = nullptr;
for(auto& nitr : _new_chain)
{
if(nitr.at(0).tid == itr.at(0).tid && nitr.at(0).get_overlap(itr.at(0)) <= 0)
{
_append_chain = &nitr;
break;
}
}
if(_append_chain)
{
for(auto& oitr : itr)
_append_chain->emplace_back(oitr);
std::sort(_append_chain->begin(), _append_chain->end(),
[](const entry& lhs, const entry& rhs) {
return lhs.get_cost() > rhs.get_cost();
});
}
else
{
_new_chain.emplace_back(std::move(itr));
}
itr.clear();
}
_chain = _new_chain;*/
}
template <typename ArchiveT, typename T, typename AllocatorT>
void
serialize_graph(ArchiveT& ar, const tim::graph<T, AllocatorT>& t)
{
OMNITRACE_CT_DEBUG("\n");
namespace cereal = tim::cereal;
using iterator_t = typename tim::graph<T, AllocatorT>::sibling_iterator;
ar(cereal::make_nvp("graph_nodes", t.size()));
ar.setNextName("graph");
ar.startNode();
ar.makeArray();
for(iterator_t itr = t.begin(); itr != t.end(); ++itr)
serialize_subgraph(ar, t, itr);
ar.finishNode();
}
template <typename ArchiveT, typename T, typename AllocatorT>
void
serialize_subgraph(ArchiveT& ar, const tim::graph<T, AllocatorT>& _graph,
typename tim::graph<T, AllocatorT>::iterator _root)
{
using iterator_t = typename tim::graph<T, AllocatorT>::sibling_iterator;
if(_graph.empty()) return;
ar.setNextName("node");
ar.startNode();
ar(*_root);
{
ar.setNextName("children");
ar.startNode();
ar.makeArray();
for(iterator_t itr = _graph.begin(_root); itr != _graph.end(_root); ++itr)
serialize_subgraph(ar, _graph, itr);
ar.finishNode();
}
ar.finishNode();
}
template <Device DevT>
std::vector<call_chain>
get_top(const std::vector<call_chain>& _chain, size_t _count)
{
OMNITRACE_CT_DEBUG("\n");
std::vector<call_chain> _data{};
_data.reserve(_count);
for(const auto& itr : _chain)
{
if(_data.size() >= _count) break;
if(itr.query<>([](const entry& _v) {
return (DevT == Device::ANY) ? true : (_v.device == DevT);
}))
{
_data.emplace_back(itr);
}
}
return _data;
}
template <Device DevT>
void
generate_perfetto(const std::vector<call_chain>& _data)
{
OMNITRACE_CT_DEBUG("\n");
auto _nrows = std::min<size_t>(get_critical_trace_per_row(), _data.size());
// run in separate thread(s) so that it ends up in unique row
if(_nrows < 1) _nrows = _data.size();
std::string _dev = (DevT == Device::NONE) ? ""
: (DevT == Device::ANY) ? "CPU + GPU "
: (DevT == Device::CPU) ? "CPU "
: "GPU ";
std::string _cpname = _dev + "CritPath";
auto _func = [&](size_t _idx, size_t _beg, size_t _end) {
if(DevT != Device::NONE)
{
if(_nrows != 1)
threading::set_thread_name(TIMEMORY_JOIN(" ", _cpname, _idx).c_str());
else
threading::set_thread_name(_cpname.c_str());
}
// ensure all hash ids exist
copy_hash_ids();
std::set<entry> _used{};
for(size_t i = _beg; i < _end; ++i)
{
if(i >= _data.size()) break;
_data.at(i).generate_perfetto<DevT>(_used);
}
};
for(size_t i = 0; i < _data.size(); i += _nrows)
{
if(DevT == Device::NONE)
_func(i, i, i + _nrows);
else
std::thread{ _func, i, i, i + _nrows }.join();
}
}
template <typename Tp, template <typename...> class ContainerT, typename... Args,
typename FuncT = bool (*)(const Tp&, const Tp&)>
inline Tp*
find(
const Tp& _v, ContainerT<Tp, Args...>& _vec,
FuncT&& _func = [](const Tp& _lhs, const Tp& _rhs) { return (_lhs == _rhs); })
{
for(auto& itr : _vec)
{
if(std::forward<FuncT>(_func)(_v, itr)) return &itr;
}
return nullptr;
};
template <typename FuncT = bool (*)(const entry&, const entry&)>
inline entry*
find(
const entry& _v, call_chain& _vec,
FuncT&& _func = [](const entry& _lhs, const entry& _rhs) { return (_lhs == _rhs); })
{
return find(_v, reinterpret_cast<std::vector<entry>&>(_vec),
std::forward<FuncT>(_func));
}
void
squash_critical_path(call_chain& _targ)
{
OMNITRACE_CT_DEBUG("\n");
static auto _strict_equal = [](const entry& _lhs, const entry& _rhs) {
auto _same_phase = (_lhs.phase == _rhs.phase);
bool _phase_check = true;
if(_same_phase) _phase_check = (_lhs.get_timestamp() == _rhs.get_timestamp());
return (_lhs == _rhs && _lhs.parent_cid == _rhs.parent_cid && _phase_check);
};
std::sort(_targ.begin(), _targ.end());
call_chain _squashed{};
for(auto& itr : _targ)
{
if(itr.phase == Phase::DELTA)
{
_squashed.emplace_back(itr);
}
else if(itr.phase == Phase::BEGIN)
{
if(!find(itr, _squashed, _strict_equal)) _squashed.emplace_back(itr);
}
else
{
entry* _match = nullptr;
if((_match = find(itr, _squashed)) != nullptr)
*_match += itr;
else
_squashed.emplace_back(itr);
}
}
std::swap(_targ, _squashed);
std::sort(_targ.begin(), _targ.end());
}
void
compute_critical_trace()
{
OMNITRACE_CT_DEBUG_F("Generating critical trace...\n");
// ensure all hash ids exist
copy_hash_ids();
using perfstats_t =
tim::lightweight_tuple<comp::wall_clock, comp::cpu_clock, comp::cpu_util,
comp::peak_rss, comp::page_rss>;
perfstats_t _ct_perf{};
_ct_perf.start();
auto _report_perf = [](auto& _perf, const char* _func, const std::string& _label) {
_perf.stop().rekey(_label);
OMNITRACE_BASIC_PRINT("[%s] %s\n", _func, JOIN("", _perf).substr(5).c_str());
OMNITRACE_BASIC_PRINT("\n");
_perf.reset().start();
};
OMNITRACE_BASIC_PRINT("\n");
try
{
PTL::ThreadPool _tp{ get_critical_trace_num_threads(), []() { copy_hash_ids(); },
[]() {} };
_tp.set_verbose(-1);
PTL::TaskGroup<void> _tg{ &_tp };
perfstats_t _perf{};
_perf.start();
OMNITRACE_BASIC_PRINT_F("sorting %zu call chain entries\n",
complete_call_chain.size());
// sort the complete call chain
std::sort(complete_call_chain.begin(), complete_call_chain.end());
_report_perf(_perf, __FUNCTION__, "sorting call chain");
OMNITRACE_BASIC_PRINT_F("squashing call chain...\n");
// squash the critical path (combine start/stop into delta)
squash_critical_path(complete_call_chain);
_report_perf(_perf, __FUNCTION__, "squashing critical path");
// generate the perfetto
if(config::get_use_perfetto())
{
OMNITRACE_BASIC_PRINT_F("generating perfetto for call chain...\n");
generate_perfetto<Device::NONE>({ complete_call_chain });
generate_perfetto<Device::CPU>({ complete_call_chain });
generate_perfetto<Device::GPU>({ complete_call_chain });
_report_perf(_perf, __FUNCTION__, "perfetto generation");
}
OMNITRACE_BASIC_PRINT_F("finding children...\n");
call_graph_t _graph{};
find_children(_tp, _graph, complete_call_chain);
_report_perf(_perf, __FUNCTION__, "finding children");
// sort the call-graph based on cost
OMNITRACE_BASIC_PRINT_F("sorting %zu call-graph entries...\n", _graph.size() - 1);
_graph.sort([](auto lhs, auto rhs) { return lhs.get_cost() > rhs.get_cost(); },
[&_tg](auto _f) { _tg.run(_f); }, [&_tg]() { _tg.join(); });
_report_perf(_perf, __FUNCTION__, "call-graph sort");
OMNITRACE_BASIC_PRINT_F("saving call-graph...\n");
save_call_graph(tim::settings::compose_output_filename("call-graph", ".json"),
"call_graph", _graph, true, __FUNCTION__);
_report_perf(_perf, __FUNCTION__, "saving call-graph");
OMNITRACE_BASIC_PRINT_F("finding sequences...\n");
std::vector<call_chain> _top{};
find_sequences(_tp, _graph, _top);
_report_perf(_perf, __FUNCTION__, "call-graph sequence search");
OMNITRACE_BASIC_PRINT_F("number of sequences found: %zu (%zu)...\n", _top.size(),
(_top.empty()) ? 0 : _top.at(0).size());
if(get_critical_trace_count() == 0)
{
OMNITRACE_CT_DEBUG_F("saving critical trace...\n");
save_critical_trace(
tim::settings::compose_output_filename("critical-trace", ".json"),
"critical_trace", _top, true, __FUNCTION__);
}
else
{
// get the top CPU critical traces
OMNITRACE_BASIC_PRINT_F("getting top CPU functions...\n");
auto _top_cpu = get_top<Device::CPU>(_top, get_critical_trace_count());
// get the top GPU critical traces
OMNITRACE_BASIC_PRINT_F("getting top GPU functions...\n");
auto _top_gpu = get_top<Device::GPU>(_top, get_critical_trace_count());
// get the top CPU + GPU critical traces
OMNITRACE_BASIC_PRINT_F("getting top CPU + GPU functions...\n");
auto _top_any = get_top<Device::ANY>(_top, get_critical_trace_count());
if(!_top_cpu.empty())
{
OMNITRACE_BASIC_PRINT_F(
"generating %zu perfetto CPU critical traces...\n", _top_cpu.size());
if(config::get_use_perfetto()) generate_perfetto<Device::CPU>(_top_cpu);
OMNITRACE_CT_DEBUG_F("saving CPU critical traces...\n");
save_critical_trace(
tim::settings::compose_output_filename("critical-trace-cpu", ".json"),
"critical_trace", _top_cpu, true, __FUNCTION__);
}
if(!_top_gpu.empty())
{
OMNITRACE_BASIC_PRINT_F(
"generating %zu perfetto GPU critical traces...\n", _top_gpu.size());
if(config::get_use_perfetto()) generate_perfetto<Device::GPU>(_top_gpu);
OMNITRACE_CT_DEBUG_F("saving GPU critical traces...\n");
save_critical_trace(
tim::settings::compose_output_filename("critical-trace-gpu", ".json"),
"critical_trace", _top_gpu, true, __FUNCTION__);
}
if(!_top_any.empty())
{
OMNITRACE_BASIC_PRINT_F(
"generating %zu perfetto CPU + GPU critical traces...\n",
_top_gpu.size());
if(config::get_use_perfetto()) generate_perfetto<Device::ANY>(_top_gpu);
OMNITRACE_CT_DEBUG_F("saving CPU + GPU critical traces...\n");
save_critical_trace(
tim::settings::compose_output_filename("critical-trace-any", ".json"),
"critical_trace", _top_any, true, __FUNCTION__);
}
}
_tg.join();
_tp.destroy_threadpool();
} catch(std::exception& e)
{
OMNITRACE_BASIC_PRINT("Thread exited '%s' with exception: %s\n", __FUNCTION__,
e.what());
TIMEMORY_CONDITIONAL_DEMANGLED_BACKTRACE(true, 32);
}
_report_perf(_ct_perf, __FUNCTION__, "critical trace computation");
}
} // namespace
} // namespace critical_trace
} // namespace omnitrace
+113
Voir le fichier
@@ -0,0 +1,113 @@
// 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 "library/config.hpp"
#include "library/critical_trace.hpp"
#include "library/debug.hpp"
#include "library/defines.hpp"
#include "library/perfetto.hpp"
#include "library/ptl.hpp"
#include <PTL/ThreadPool.hh>
#include <timemory/backends/dmp.hpp>
#include <timemory/backends/threading.hpp>
#include <timemory/hash/types.hpp>
#include <timemory/tpls/cereal/cereal/archives/json.hpp>
#include <timemory/tpls/cereal/cereal/cereal.hpp>
#include <timemory/utility/macros.hpp>
#include <timemory/utility/types.hpp>
#include <timemory/utility/utility.hpp>
#include <cctype>
#include <cstdint>
#include <exception>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <utility>
namespace omnitrace
{
namespace critical_trace
{
namespace
{
using call_graph_t = tim::graph<entry>;
using call_graph_itr_t = typename call_graph_t::iterator;
using call_graph_sibling_itr_t = typename call_graph_t::sibling_iterator;
using call_graph_preorder_itr_t = typename call_graph_t::pre_order_iterator;
hash_ids complete_hash_ids{};
call_chain complete_call_chain{};
std::mutex complete_call_mutex{};
void
update_critical_path(call_chain _chain, int64_t _tid);
void
load_call_chain(const std::string& _fname, const std::string& _label,
call_chain& _call_chain);
void
compute_critical_trace();
void
find_children(PTL::ThreadPool& _tp, call_graph_t& _graph, const call_chain& _chain);
void
find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph,
std::vector<call_chain>& _chain);
void
find_sequences(PTL::ThreadPool& _tp, call_graph_t& _graph, call_graph_itr_t _root,
std::vector<call_chain>& _chain);
template <typename ArchiveT, typename T, typename AllocatorT>
void
serialize_graph(ArchiveT& ar, const tim::graph<T, AllocatorT>& _graph);
template <typename ArchiveT, typename T, typename AllocatorT>
void
serialize_subgraph(ArchiveT& ar, const tim::graph<T, AllocatorT>& _graph,
typename tim::graph<T, AllocatorT>::iterator _root);
void
compute_critical_trace();
template <Device DevT>
void
generate_perfetto(const std::vector<call_chain>& _data);
inline void
copy_hash_ids()
{
// make copy to avoid parallel iteration issues
auto _hash_ids = complete_hash_ids;
// ensure all hash ids exist
for(const auto& itr : _hash_ids)
tim::hash::add_hash_id(itr);
}
} // namespace
} // namespace critical_trace
} // namespace omnitrace
+37
Voir le fichier
@@ -0,0 +1,37 @@
# ------------------------------------------------------------------------------#
#
# omnitrace-exe target
#
# ------------------------------------------------------------------------------#
add_executable(
omnitrace-exe
${_EXCLUDE} ${CMAKE_CURRENT_LIST_DIR}/omnitrace.cpp
${CMAKE_CURRENT_LIST_DIR}/omnitrace.hpp ${CMAKE_CURRENT_LIST_DIR}/details.cpp)
target_link_libraries(
omnitrace-exe
PRIVATE omnitrace::omnitrace-headers
omnitrace::omnitrace-dyninst
omnitrace::omnitrace-compile-options
$<BUILD_INTERFACE:timemory::timemory-headers>
$<IF:$<BOOL:${OMNITRACE_USE_SANITIZER}>,omnitrace::omnitrace-sanitizer,>)
set_target_properties(
omnitrace-exe
PROPERTIES
OUTPUT_NAME omnitrace
INSTALL_RPATH_USE_LINK_PATH ON
INSTALL_RPATH
"\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/timemory/libunwind:\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}/dyninst-tpls/lib"
)
if(CMAKE_BUILD_TYPE MATCHES "^(DEBUG|Debug)")
string(REPLACE " " ";" _FLAGS "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
target_compile_options(omnitrace-exe PRIVATE ${_FLAGS})
endif()
install(
TARGETS omnitrace-exe
DESTINATION ${CMAKE_INSTALL_BINDIR}
OPTIONAL)
+916
Voir le fichier
@@ -0,0 +1,916 @@
// 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 "omnitrace.hpp"
static int expect_error = NO_ERROR;
static int error_print = 0;
static auto regex_opts = std::regex_constants::egrep | std::regex_constants::optimize;
// set of whole function names to exclude
strset_t
get_whole_function_names()
{
return strset_t{ "a64l",
"advance",
"aio_return",
"aio_return64",
"argp_error",
"argp_failure",
"argp_help",
"argp_parse",
"argp_state_help",
"argp_usage",
"argz_add",
"argz_add_sep",
"argz_append",
"argz_count",
"argz_create",
"argz_create_sep",
"argz_delete",
"argz_extract",
"argz_insert",
"argz_next",
"argz_replace",
"argz_stringify",
"atexit",
"atof",
"atoi",
"atol",
"atoll",
"atomic_flag_clear_explicit",
"atomic_flag_test_and_set_explicit",
"authdes_create",
"authdes_getucred",
"authdes_pk_create",
"authnone_create",
"authunix_create",
"authunix_create_default",
"backtrace",
"backtrace_symbols",
"backtrace_symbols_fd",
"bindresvport",
"bindtextdomain",
"bind_textdomain_codeset",
"bsearch",
"btowc",
"c16rtomb",
"callrpc",
"canonicalize_file_name",
"catclose",
"catgets",
"catopen",
"cfmakeraw",
"cfsetspeed",
"chflags",
"clearerr",
"clearerr_unlocked",
"clnt_broadcast",
"clnt_create",
"clnt_pcreateerror",
"clnt_perrno",
"clnt_perror",
"clntraw_create",
"clnt_spcreateerror",
"clnt_sperrno",
"clnt_sperror",
"clnttcp_create",
"clntudp_bufcreate",
"clntudp_create",
"clntunix_create",
"confstr",
"daemon",
"des_setparity",
"div",
"dlopen",
"dlsym",
"dlerror",
"dladdr",
"dlinfo",
"dlvsym",
"dlmopen",
"dl_iterate_phdr",
"dysize",
"endutxent",
"envz_add",
"envz_entry",
"envz_get",
"envz_merge",
"envz_remove",
"envz_strip",
"ether_aton",
"ether_hostton",
"ether_line",
"ether_ntoa",
"ether_ntohost",
"execl",
"execle",
"execlp",
"execv",
"execvp",
"execvpe",
"explicit_bzero",
"fattach",
"fclose",
"fdetach",
"fdopen",
"feof_unlocked",
"ferror_unlocked",
"fflush",
"fflush_unlocked",
"fgetpos",
"fgets",
"fgets_unlocked",
"fgetws",
"fgetws_unlocked",
"_fini",
"fini",
"fmemopen",
"fopen",
"fopen64",
"fopencookie",
"fork",
"fork_alias",
"fork_compat",
"fputc_unlocked",
"fputs",
"fputs_unlocked",
"fputwc_unlocked",
"fputws",
"fputws_unlocked",
"fread",
"fread_unlocked",
"fsetpos",
"fsetpos64",
"ftell",
"fwrite",
"fwrite_unlocked",
"getdelim",
"getgrouplist",
"gethostbyname2",
"getmntent",
"getmsg",
"getnetname",
"getopt_long",
"getopt_long_only",
"getpmsg",
"getpublickey",
"gets",
"getsecretkey",
"glob_pattern_p",
"gnu_dev_major",
"gnu_dev_makedev",
"gnu_dev_minor",
"gnu_get_libc_release",
"gnu_get_libc_version",
"group_member",
"gtty",
"hcreate",
"hdestroy",
"herror",
"host2netname",
"hsearch",
"hstrerror",
"htons",
"iconv",
"iconv_close",
"iconv_open",
"inet6_opt_append",
"inet6_opt_find",
"inet6_opt_finish",
"inet6_opt_get_val",
"inet6_opt_init",
"inet6_option_alloc",
"inet6_option_append",
"inet6_option_find",
"inet6_option_init",
"inet6_option_next",
"inet6_option_space",
"inet6_opt_next",
"inet6_opt_set_val",
"inet6_rth_add",
"inet6_rth_getaddr",
"inet6_rth_init",
"inet6_rth_reverse",
"inet6_rth_segments",
"inet6_rth_space",
"inet_addr",
"inet_aton",
"inet_lnaof",
"inet_makeaddr",
"inet_netof",
"inet_network",
"inet_nsap_addr",
"inet_nsap_ntoa",
"inet_ntoa",
"inet_ntop",
"inet_pton",
"_init",
"init",
"initgroups",
"initstate",
"insque",
"iruserok",
"iruserok_af",
"key_decryptsession",
"key_decryptsession_pk",
"key_encryptsession",
"key_encryptsession_pk",
"key_gendes",
"key_get_conv",
"key_secretkey_is_set",
"key_setnet",
"key_setsecret",
"l64a",
"lchmod",
"lckpwdf",
"lfind",
"llabs",
"lldiv",
"localeconv",
"lockf",
"lsearch",
"mbrtoc16",
"mbrtoc32",
"mcheck",
"mcheck_check_all",
"mcheck_pedantic",
"mkdtemp",
"mkdtemp64",
"mkostemp",
"mkostemp64",
"mkostemps",
"mkostemps64",
"mkstemp",
"mkstemp64",
"mkstemps",
"mkstemps64",
"mktemp",
"mktemp64",
"moncontrol",
"monstartup",
"mprobe",
"mtrace",
"muntrace",
"nanosleep",
"netname2host",
"netname2user",
"nl_langinfo",
"nl_langinfo_l",
"ntohs",
"parse_printf_format",
"passwd2des",
"pclose",
"perror",
"pmap_getmaps",
"pmap_getport",
"pmap_rmtcall",
"pmap_set",
"pmap_unset",
"popen",
"printf_size",
"printf_size_info",
"psiginfo",
"psignal",
"putchar",
"putchar_unlocked",
"putc_unlocked",
"putenv",
"putgrent",
"putmsg",
"putpmsg",
"putpwent",
"puts",
"putsgent",
"putspent",
"pututxline",
"putw",
"putwc",
"putwchar",
"putwchar_unlocked",
"putwc_unlocked",
"rcmd",
"rcmd_af",
"reallocarray",
"realpath",
"re_comp",
"re_compile_fastmap",
"re_compile_pattern",
"re_exec",
"regcomp",
"regerror",
"regexec",
"register_printf_modifier",
"register_printf_type",
"registerrpc",
"re_match",
"re_match_2",
"remque",
"re_search",
"re_search_2",
"re_set_registers",
"re_set_syntax",
"revoke",
"rexec",
"rexec_af",
"rpmatch",
"rresvport",
"rresvport_af",
"ruserok",
"ruserok_af",
"ruserpass",
"secure_getenv",
"seed48",
"setbuffer",
"setstate",
"setvbuf",
"sgetsgent",
"sgetspent",
"sigcancel_handler",
"sighandler_setxid",
"sstk",
"step",
"stty",
"svcerr_auth",
"svcerr_decode",
"svcerr_noproc",
"svcerr_noprog",
"svcerr_progvers",
"svcerr_systemerr",
"svcerr_weakauth",
"svc_exit",
"svcfd_create",
"svc_getreq",
"svc_getreq_common",
"svc_getreq_poll",
"svc_getreqset",
"svcraw_create",
"svc_register",
"svc_run",
"svc_sendreply",
"svctcp_create",
"svcudp_bufcreate",
"svcudp_create",
"svcudp_enablecache",
"svcunix_create",
"svcunixfd_create",
"svc_unregister",
"swab",
"tcgetsid",
"tdelete",
"tdestroy",
"tempnam",
"textdomain",
"tfind",
"thrd_create",
"thrd_current",
"thrd_detach",
"thrd_equal",
"thrd_exit",
"thrd_join",
"thrd_sleep",
"thrd_yield",
"tmpnam",
"tolower",
"toupper",
"towctrans",
"towctrans_l",
"tr_break",
"tsearch",
"tss_create",
"tss_delete",
"tss_get",
"tss_set",
"ttyslot",
"twalk",
"twalk_r",
"tzset",
"ulckpwdf",
"ungetc",
"ungetwc",
"unwind_stop",
"updwtmpx",
"user2netname",
"utmpname",
"utmpxname",
"vlimit",
"vtimes",
"wait",
"wait3",
"waitpid",
"wordexp",
"xdecrypt",
"xdr_accepted_reply",
"xdr_array",
"xdr_authdes_cred",
"xdr_authdes_verf",
"xdr_authunix_parms",
"xdr_bool",
"xdr_bytes",
"xdr_callhdr",
"xdr_callmsg",
"xdr_char",
"xdr_cryptkeyarg",
"xdr_cryptkeyarg2",
"xdr_cryptkeyres",
"xdr_des_block",
"xdr_double",
"xdr_enum",
"xdr_float",
"xdr_getcredres",
"xdr_hyper",
"xdr_int",
"xdr_int16_t",
"xdr_int64_t",
"xdr_int8_t",
"xdr_keybuf",
"xdr_key_netstarg",
"xdr_key_netstres",
"xdr_keystatus",
"xdr_longlong_t",
"xdrmem_create",
"xdr_netnamestr",
"xdr_netobj",
"xdr_opaque",
"xdr_opaque_auth",
"xdr_pmap",
"xdr_pmaplist",
"xdr_pointer",
"xdr_quad_t",
"xdrrec_create",
"xdrrec_endofrecord",
"xdrrec_eof",
"xdrrec_skiprecord",
"xdr_reference",
"xdr_rejected_reply",
"xdr_replymsg",
"xdr_rmtcall_args",
"xdr_rmtcallres",
"xdr_short",
"xdr_sizeof",
"xdrstdio_create",
"xdr_string",
"xdr_u_char",
"xdr_u_hyper",
"xdr_u_int",
"xdr_uint16_t",
"xdr_uint64_t",
"xdr_uint8_t",
"xdr_u_long",
"xdr_u_longlong_t",
"xdr_union",
"xdr_unixcred",
"xdr_u_quad_t",
"xdr_u_short",
"xdr_vector",
"xdr_void",
"xdr_wrapstring",
"xencrypt",
"xprt_register",
"xprt_unregister" };
}
//======================================================================================//
//
// For selective instrumentation (unused)
//
bool
are_file_include_exclude_lists_empty()
{
return true;
}
//======================================================================================//
//
// Gets information (line number, filename, and column number) about
// 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,
basic_loop_t* loopToInstrument)
{
if(!cfGraph || !loopToInstrument || !f) return function_signature{ "", "", "" };
char fname[FUNCNAMELEN + 1];
char mname[FUNCNAMELEN + 1];
std::string typeName = {};
memset(fname, '\0', FUNCNAMELEN + 1);
memset(mname, '\0', FUNCNAMELEN + 1);
mutatee_module->getName(mname, FUNCNAMELEN);
bpvector_t<point_t*>* loopStartInst =
cfGraph->findLoopInstPoints(BPatch_locLoopStartIter, loopToInstrument);
bpvector_t<point_t*>* loopExitInst =
cfGraph->findLoopInstPoints(BPatch_locLoopEndIter, loopToInstrument);
if(!loopStartInst || !loopExitInst) return function_signature{ "", "", "" };
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();
std::vector<string_t> _params;
if(params)
{
for(auto* itr : *params)
{
string_t _name = itr->getType()->getName();
if(_name.empty()) _name = itr->getName();
_params.push_back(_name);
}
}
bpvector_t<BPatch_statement> lines;
bpvector_t<BPatch_statement> linesEnd;
bool info1 = mutatee_module->getSourceLines(baseAddr, lines);
string_t filename = mname;
if(info1)
{
// filename = lines[0].fileName();
auto row1 = lines[0].lineNumber();
auto col1 = lines[0].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());
if(info2)
{
auto row2 = linesEnd[0].lineNumber();
auto col2 = linesEnd[0].lineOffset();
if(col2 < 0) col2 = 0;
if(row2 < row1) row1 = row2; // Fix for wrong line numbers
return function_signature(typeName, fname, filename, _params, { row1, row2 },
{ col1, col2 }, true, info1, info2);
}
else
{
return function_signature(typeName, fname, filename, _params, { row1, 0 },
{ col1, 0 }, true, info1, info2);
}
}
else
{
return function_signature(typeName, fname, filename, _params);
}
}
//======================================================================================//
//
// 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)
{
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 = {};
memset(fname, '\0', FUNCNAMELEN + 1);
memset(mname, '\0', FUNCNAMELEN + 1);
mutatee_module->getName(mname, FUNCNAMELEN);
baseAddr = (unsigned long) (f->getBaseAddr());
f->getAddressRange(baseAddr, lastAddr);
bpvector_t<BPatch_statement> lines;
f->getName(fname, FUNCNAMELEN);
auto* returnType = f->getReturnType();
if(returnType)
{
typeName = returnType->getName();
}
auto* params = f->getParams();
std::vector<string_t> _params;
if(params)
{
for(auto* itr : *params)
{
string_t _name = itr->getType()->getName();
if(_name.empty()) _name = itr->getName();
_params.push_back(_name);
}
}
info1 = mutatee_module->getSourceLines((unsigned long) baseAddr, lines);
filename = mname;
if(info1)
{
// 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);
}
}
else
{
return function_signature(typeName, fname, filename, _params, { 0, 0 }, { 0, 0 },
false, false, false);
}
}
//======================================================================================//
//
// Error callback routine.
//
void
errorFunc(error_level_t level, int num, const char** params)
{
char line[256];
const char* msg = bpatch->getEnglishErrorString(num);
bpatch->formatErrorString(line, sizeof(line), msg, params);
if(num != expect_error)
{
printf("Error #%d (level %d): %s\n", num, level, line);
// We consider some errors fatal.
if(num == 101) exit(-1);
}
}
//======================================================================================//
//
// For compatibility purposes
//
procedure_t*
find_function(image_t* app_image, const std::string& _name, const strset_t& _extra)
{
if(_name.empty()) return nullptr;
auto _find = [app_image](const string_t& _f) -> procedure_t* {
// Extract the vector of functions
bpvector_t<procedure_t*> _found;
auto* ret = app_image->findFunction(_f.c_str(), _found, false, true, true);
if(ret == nullptr || _found.empty()) return nullptr;
return _found.at(0);
};
procedure_t* _func = _find(_name);
auto itr = _extra.begin();
while(_func == nullptr && itr != _extra.end())
{
_func = _find(*itr);
++itr;
}
if(!_func)
{
verbprintf(1, "function: '%s' ... not found\n", _name.c_str());
}
else
{
verbprintf(1, "function: '%s' ... found\n", _name.c_str());
}
return _func;
}
//======================================================================================//
//
void
error_func_real(error_level_t level, int num, const char* const* params)
{
if(num == 0)
{
// conditional reporting of warnings and informational messages
if(error_print > 0)
{
if(level == BPatchInfo)
{
if(error_print > 1) printf("%s\n", params[0]);
}
else
printf("%s", params[0]);
}
}
else
{
// reporting of actual errors
char line[256];
const char* msg = bpatch->getEnglishErrorString(num);
bpatch->formatErrorString(line, sizeof(line), msg, params);
if(num != expect_error)
{
printf("Error #%d (level %d): %s\n", num, level, line);
// We consider some errors fatal.
if(num == 101) exit(-1);
}
}
}
//======================================================================================//
//
// We've a null error function when we don't want to display an error
//
void
error_func_fake(error_level_t level, int num, const char* const* params)
{
consume_parameters(level, num, params);
// It does nothing.
}
//======================================================================================//
//
bool
c_stdlib_module_constraint(const std::string& _file)
{
static std::regex _pattern(
"^(a64l|accept4|alphasort|argp-help|argp-parse|asprintf|atof|atoi|atol|atoll|"
"auth_des|auth_none|auth_unix|backtrace|backtracesyms|backtracesymsfd|c16rtomb|"
"cacheinfo|canonicalize|carg|cargf|cargf128|cargl|"
"catgets|cfmakeraw|cfsetspeed|check_pf|chflags|"
"clearerr|clearerr_u|clnt_perr|clnt_raw|clnt_tcp|clnt_udp|clnt_unix"
"settime|copy_file_range|"
"creat64|ctermid|ctime|ctime_r|ctype|ctype-c99|ctype-c99_l|ctype-extn|ctype_l|"
"cuserid|daemon|dcigettext|difftime|dirname|div|dl-error|dl-libc|dl-sym|dlerror|"
"duplocale|dysize|endutxent|envz|epoll_wait|"
"ether_aton|ether_aton_r|ether_hton|ether_line|ether_ntoa|ether_ntoh|eventfd_"
"read|eventfd_write|execlp|execv|execvp|explicit_bzero|faccessat|fallocate64|"
"fattach|fchflags|fchmodat|fdatasync|fdetach|fdopendir|fedisblxcpt|feenablxcpt|"
"fegetexcept|fegetmode|feholdexcpt|feof_u|ferror_u|fesetenv|fesetexcept|"
"fesetmode|fesetround|fetestexceptflag|fexecve|ffsll|fgetexcptflg|fgetgrent|"
"fgetpwent|fgetsgent|fgetspent|fileno|fmemopen|fmtmsg|fnmatch|fprintf|fputc|"
"fputc_u|fputwc|fputwc_u|freopen|freopen64|fscanf|fseeko|fsetexcptflg|fstab|"
"fsync|ftello|ftime|ftok|fts|ftw|futimens|futimesat|fwide|fxprintf|gconv_conf|"
"gconv_db|gconv_dl|genops|getaddrinfo|getaliasent|getaliasent_r|getaliasname|"
"getauxval|getc|getchar|getchar_u|getdate|getdirentries|getdirname|getentropy|"
"getenv|getgrent|getgrent_r|getgrgid|getgrnam|gethostid|gethstbyad|gethstbynm|"
"gethstbynm2|gethstent|gethstent_r|getipv4sourcefilter|getloadavg|getlogin|"
"getlogin_r|getmsg|getnameinfo|getnetbyad|getnetbynm|getnetent|getnetent_r|"
"getnetgrent|getnetgrent_r|getopt|getopt1|getpass|getproto|getprtent|getprtent_r|"
"getprtname|getpwent|getpwent_r|getpwnam|getpwnam_r|getpwuid|getrandom|"
"getrpcbyname|getrpcbynumber|getrpcent|getrpcent_r|getrpcport|getservent|"
"getservent_r|getsgent|getsgent_r|getsgnam|getsourcefilter|getspent|getspent_r|"
"getspnam|getsrvbynm|getsrvbynm_r|getsrvbypt|getsubopt|getsysstats|getttyent|"
"getusershell|getutent_r|getutline|getutmp|getutxent|getutxid|getutxline|getw|"
"getwchar|getwchar_u|getwd|glob|gmon|gmtime|grantpt|group_member|gtty|herror|"
"hsearch|hsearch_r|htons|iconv|iconv_close|iconv_open|idn-stub|if_index|ifaddrs|"
"inet6_|inet_|inet_|initgroups|insremque|iofgets|iofgetws|iofgetws_u|iofputws|"
"iofwide|iopopen|ioungetwc|isastream|isctype|isfdtype|key_call|key_prot|killpg|"
"l64a|labs|lchmod|lckpwdf|lcong48|ldiv|llabs|lldiv|lockf|longjmp|lsearch|lutimes|"
"makedev|malloc|mblen|mbrtoc16|mbsinit|mbstowcs|mbtowc|mcheck|memccpy|"
"memchr|memcmp|memfrob|memmem|memset|memstream|mkdtemp|mkfifo|mkfifoat|mkostemp|"
"mkostemps|mkstemp|mkstemps|mktemp|mlock2|mntent|mntent_r|mpa|"
"msgctl|msgget|msgsnd|msort|msync|mtrace|netname|nice|nl_langinfo|nsap_addr|nscd_"
"getgr_r|nscd_gethst_r|nscd_getpw_r|nscd_getserv_r|nscd_helper|"
"nsswitch|ntp_gettime|ntp_gettimex|obprintf|obstack|oldfmemopen|open_by_handle_"
"at|opendir|pathconf|pclose|perror|pkey_mprotect|pm_getmaps|pmap_prot|pmap_rmt|"
"posix_fallocate|posix_fallocate64|preadv64|preadv64v2|printf-prs|printf_fp|"
"printf_size|profil|psiginfo|psignal|ptrace|ptsname|putc_u|putchar|putchar_u|"
"putenv|putgrent|putmsg|putpwent|putsgent|putspent|pututxline|putw|putwc_u|"
"putwchar|putwchar_u|pwritev64|pwritev64v2|raise|rcmd|readv|"
"reboot|recvfrom|recvmmsg|regex|regexp|remove|rename|renameat|res-close|res_"
"hconf|res_init|resolv_conf|rexec|rpc_thread|rpmatch|ruserpass|scandir|sched_"
"cpucount|sched_getaffinity|sched_getcpu|seed48|seekdir|semget|semop|semtimedop|"
"sendmsg|setbuf|setegid|seteuid|sethostid|setipv4sourcefilter|setlinebuf|"
"setlogin|setpgrp|setresuid|setrlimit64|setsourcefilter|setutxent|sgetsgent|"
"sgetspent|shmat|shmdt|shmget|sigandset|sigdelset|siggetmask|sighold|sigignore|"
"sigintr|sigisempty|signalfd|sigorset|sigpause|sigpending|sigrelse|sigset|"
"sigstack|sockatmark|speed|splice|sprofil|sscanf|sstk|stime|strcasecmp|"
"strcasestr|strcat|strchr|strcmp|strcpy|strcspn|strerror|strerror_l|strfmon|"
"strfromd|strfromf|strfromf128|strfroml|strfry|strlen|strncase|strncat|strncmp|"
"strncpy|strpbrk|strrchr|strsignal|strspn|strstr|strtod_l|strtof|strtof128_l|"
"strtof_l|strtoimax|strtok|strtol_l|strtold_l|strtoul|strtoumax|strxfrm|stty|svc|"
"svc_raw|svc_simple|svc_tcp|svc_udp|svc_unix|swab|sync_file_range|syslog|system|"
"tcflow|tcflush|tcgetattr|tcgetsid|tcsendbrk|tcsetpgrp|tee|telldir|tempnam|"
"tmpnam|tmpnam_r|tsearch|ttyname|ttyname_r|ttyslot|tzset|ualarm|ulimit|umount|"
"unlockpt|updwtmpx|ustat|utimensat|utmp_file|utmpxname|version|"
"versionsort|vfprintf|vfscanf|vfwscanf|vlimit|vmsplice|vprintf|vtimes|wait[0-9]|"
"wcfuncs|wcfuncs_l|wcscpy|wcscspn|wcsdup|wcsncat|wcsncmp|wcsnrtombs|wcspbrk|"
"wcsrchr|wcsstr|wcstod_l|wcstof|wcstoimax|wcstok|wcstold_l|wcstombs|wcstoumax|"
"wcswidth|wcsxfrm|wctob|wctype_l|wcwidth|wfileops|wgenops|wmemcmp|wmemstream|"
"wordexp|wstrops|x2y2m1l|xcrypt|xdr|xdr_float|xdr_intXX_t|xdr_mem|xdr_rec|xdr_"
"ref|xdr_sizeof|xdr_stdio|mq_notify|aio_|timer_routines|nptl-|shm-|sem_close|"
"setuid|pt-raise|x2y2)",
regex_opts);
return std::regex_search(_file, _pattern);
}
//======================================================================================//
//
bool
c_stdlib_function_constraint(const std::string& _func)
{
static std::regex _pattern(
"^(malloc|calloc|free|buffer|fscan|fstab|internal|gnu|fprint|isalnum|isalpha|"
"isascii|isastream|isblank|isblank_l|iscntrl|isctype|isdigit|isdigit_l|isfdtype|"
"isgraph|islower|islower_l|isprint|isprint_l|ispunct|isspace|isupper|isupper_l|"
"iswprint|isxdigit|asprintf|atof|atoi|atol|atoll|memalign|memccpy|memcpy|memchr|"
"memcmp|memfrob|memset|mkdtemp|mkfifo|mkfifoat|mkostemp64|mkostemps64|mkstemp|"
"mkstemps64|mktemp|mlock2|monstartup|mprobe|mremap_chunk|get_current_dir_name|"
"get_free_list|getaliasbyname|getaliasent|getauxval|getchar|getchar_unlocked|"
"getdate|getdirentries|getentropy|getenv|getfs|getgrent|getgrgid|"
"getgrnam|getgrouplist|gethostbyaddr|gethostbyname|gethostbyname2|gethostent|"
"gethostid|getifaddrs|getifaddrs_internal|getipv4sourcefilter|getkeyserv_handle|"
"getloadavg|getlogin|getlogin_fd0|getlogin_r_fd0|getmntent|getmsg|getnetbyaddr|"
"getnetbyname|getnetent|getnetgrent|getopt|getopt_long|getopt_long_only|getpass|"
"getprotobyname|getprotobynumber|getprotoent|getpwent|getpwnam|getpwnam_r|"
"getpwuid|getrandom|getrpcbyname|getrpcbynumber|getrpcent|getrpcport|"
"getservbyname|getservbyname_r|getservbyport|getservent|getsgent|getsgnam|"
"getsourcefilter|getspent|getspnam|getsubopt|getttyname|getttyname_r|"
"getusershell|getutent_r_file|getutent_r_unknown|getutid_r_file|getutid_r_"
"unknown|getutline|getutline_r_file|getutline_r_unknown|getutmp|getutxent|"
"getutxid|getutxline|getw|psiginfo|psignal|ptmalloc_init|ptrace|ptsname|putc_"
"unlocked|putchar|putchar_unlocked|putenv|putgrent|putmsg|putpwent|putsgent|"
"putspent|pututline_file|pututxline|putw|pw_map_free|pwritev|pwritev2|"
"qsort|raise|rcmd|re_acquire_state|re_acquire_state_context|re_"
"comp|re_compile_internal|re_dfa_add_node|re_exec|re_node_set_init_union|re_node_"
"set_insert|re_node_set_merge|re_search_internal|re_search_stub|re_string_"
"context_at|re_string_reconstruct|readtcp|readunix|readv|realloc|realpath|str_to_"
"mpn|strcasecmp|strcat|strcmp|strcpy|strcspn|strerror|strerror_l|strerror_thread_"
"freeres|strfmon|strfromd|strfromf|strfromf128|strfroml|strfry|strlen|"
"strncasecmp|strncat|strncmp|strncpy|strpbrk|strrchr|strsignal|strspn|strtof32|"
"strtoimax|strtok|strtol_l|strtold_l|strtoull|strtoumax|strxfrm|xdrstdio|xdrmem|"
"inet_|inet6_|clock_|backtrace_|dummy_|fts_|fts64_|fexecv|execv|stime|ftime|"
"gmtime|wcs|envz_|fmem|fputc|fgetc|fputwc|fgetwc|vprintf|feget|fetest|feenable|"
"feset|fedisable|nscd_|fork|execl|tzset|ntp_|mtrace|tr_[a-z]+hook|mcheck_[a-z_]+"
"ftell|fputs|fgets|siglongjmp|sigdelset|killpg|tolower|toupper|daemon|"
"iconv_[a-z_]+|catopen|catgets|catclose|check_add_mapping$|sem_open|sem_close|"
"sem_unlink|do_futex_wait|sem_timedwait|unwind_stop|unwind_cleanup|longjmp_"
"compat|vfork_|elision_init|cr_|cri_|aio_|mq_|sem_init|waitpid$|sigcancel_"
"handler|sighandler_setxid|start_thread$|clock$|semctl$|shm_open$|shm_unlink$|"
"printf|dprintf|walker$|clear_once_control$|libcr_|sem_wait$|sem_trywait$|vfork|"
"pause$|wait$|waitid$|msgrcv$|sigwait$|sigsuspend$|recvmsg$|sendmsg$|"
"ftrylockfile$|funlockfile$|tee$|setbuf$|setbuffer$|enlarge_userbuf$|convert_and_"
"print$|feraise|lio_|atomic_|err$|errx$|print_errno_message$|error_tail$|"
"clntunix_|sem_destroy|setxid_mark_thread|feupdate|send$|connect$|longjmp|pwrite|"
"accept$|stpncpy$|writeunix$|xflowf$|mbrlen$)",
regex_opts);
return std::regex_search(_func, _pattern);
}
//======================================================================================//
//
Fichier diff supprimé car celui-ci est trop grand Voir la Diff
+716
Voir le fichier
@@ -0,0 +1,716 @@
// 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 <timemory/backends/process.hpp>
#include <timemory/environment.hpp>
#include <timemory/mpl/apply.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 <limits>
#include <memory>
#include <numeric>
#include <regex>
#include <set>
#include <sstream>
#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 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 regexvec_t func_include = {};
static regexvec_t func_exclude = {};
static regexvec_t file_include = {};
static regexvec_t file_exclude = {};
//
//======================================================================================//
// control debug printf statements
#define dprintf(...) \
if(debug_print || verbose_level > 0) \
fprintf(stderr, "[omnitrace][exe] " __VA_ARGS__); \
fflush(stderr);
// control verbose printf statements
#define verbprintf(LEVEL, ...) \
if(verbose_level >= LEVEL) fprintf(stdout, "[omnitrace][exe] " __VA_ARGS__); \
fflush(stdout);
#define verbprintf_bare(LEVEL, ...) \
if(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);
bool
c_stdlib_module_constraint(const string_t& file);
bool
c_stdlib_function_constraint(const string_t& func);
//======================================================================================//
inline string_t
get_absolute_path(const char* fname)
{
char path_save[PATH_MAX];
char abs_exe_path[PATH_MAX];
char* p = nullptr;
if(!(p = strrchr((char*) fname, '/')))
{
auto* ret = getcwd(abs_exe_path, sizeof(abs_exe_path));
consume_parameters(ret);
}
else
{
auto* rets = getcwd(path_save, sizeof(path_save));
auto retf = chdir(fname);
auto* reta = getcwd(abs_exe_path, sizeof(abs_exe_path));
auto retp = chdir(path_save);
consume_parameters(rets, retf, reta, retp);
}
return string_t(abs_exe_path);
}
//======================================================================================//
inline string_t
to_lower(string_t s)
{
for(auto& itr : s)
itr = tolower(itr);
return 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)
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 += ")";
}
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;
}
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);
}
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;
};
//
//======================================================================================//
//
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();
}
//
static inline void
dump_info(const string_t& _oname, const fmodset_t& _data, int _level, bool _fail)
{
if(!debug_print && verbose_level < _level) return;
std::ofstream ofs{ _oname };
if(ofs)
{
verbprintf(_level, "Dumping '%s'... ", _oname.c_str());
dump_info(ofs, _data);
verbprintf_bare(_level, "Done\n");
}
else
{
std::stringstream _msg{};
_msg << "[" << __FUNCTION__ << "] Error opening '" << _oname << " for output";
verbprintf(_level, "%s\n", _msg.str().c_str());
if(_fail) throw std::runtime_error(_msg.str());
}
ofs.close();
}
//
//======================================================================================//
//
template <typename Tp, std::enable_if_t<!std::is_same<Tp, std::string>::value, int> = 0>
snippet_pointer_t
get_snippet(Tp arg)
{
return std::make_shared<snippet_t>(const_expr_t{ arg });
}
//
//======================================================================================//
//
template <typename Tp, std::enable_if_t<std::is_same<Tp, std::string>::value, int> = 0>
snippet_pointer_t
get_snippet(const Tp& arg)
{
return std::make_shared<snippet_t>(const_expr_t{ arg.c_str() });
}
//
//======================================================================================//
//
template <typename... Args>
snippet_pointer_vec_t
get_snippets(Args&&... args)
{
snippet_pointer_vec_t _tmp{};
TIMEMORY_FOLD_EXPRESSION(_tmp.push_back(get_snippet(std::forward<Args>(args))));
return _tmp;
}
//
//======================================================================================//
//
struct omnitrace_call_expr
{
using snippet_pointer_t = std::shared_ptr<snippet_t>;
template <typename... Args>
omnitrace_call_expr(Args&&... args)
: m_params(get_snippets(std::forward<Args>(args)...))
{}
snippet_vec_t get_params()
{
snippet_vec_t _ret;
for(auto& itr : m_params)
_ret.push_back(itr.get());
return _ret;
}
inline call_expr_pointer_t get(procedure_t* func)
{
return call_expr_pointer_t((func) ? new call_expr_t(*func, get_params())
: nullptr);
}
private:
snippet_pointer_vec_t m_params;
};
//
//======================================================================================//
//
struct omnitrace_snippet_vec
{
using entry_type = std::vector<omnitrace_call_expr>;
using value_type = std::vector<call_expr_pointer_t>;
template <typename... Args>
void generate(procedure_t* func, Args&&... args)
{
auto _expr = omnitrace_call_expr(std::forward<Args>(args)...);
auto _call = _expr.get(func);
if(_call)
{
m_entries.push_back(_expr);
m_data.push_back(_call);
// m_data.push_back(entry_type{ _call, _expr });
}
}
void append(snippet_vec_t& _obj)
{
for(auto& itr : m_data)
_obj.push_back(itr.get());
}
private:
entry_type m_entries;
value_type m_data;
};
//
//======================================================================================//
//
static inline address_space_t*
omnitrace_get_address_space(patch_pointer_t& _bpatch, int _cmdc, char** _cmdv,
bool _rewrite, int _pid = -1, const string_t& _name = {})
{
address_space_t* mutatee = nullptr;
if(_rewrite)
{
verbprintf(1, "Opening '%s' for binary rewrite... ", _name.c_str());
fflush(stderr);
if(!_name.empty()) mutatee = _bpatch->openBinary(_name.c_str(), false);
if(!mutatee)
{
fprintf(stderr, "[omnitrace][exe] Failed to open binary '%s'\n",
_name.c_str());
throw std::runtime_error("Failed to open binary");
}
verbprintf_bare(1, "Done\n");
}
else if(_pid >= 0)
{
verbprintf(1, "Attaching to process %i... ", _pid);
fflush(stderr);
char* _cmdv0 = (_cmdc > 0) ? _cmdv[0] : nullptr;
mutatee = _bpatch->processAttach(_cmdv0, _pid);
if(!mutatee)
{
fprintf(stderr, "[omnitrace][exe] Failed to connect to process %i\n",
(int) _pid);
throw std::runtime_error("Failed to attach to process");
}
verbprintf_bare(1, "Done\n");
}
else
{
verbprintf(1, "Creating process '%s'... ", _cmdv[0]);
fflush(stderr);
mutatee = _bpatch->processCreate(_cmdv[0], (const char**) _cmdv, nullptr);
if(!mutatee)
{
std::stringstream ss;
for(int i = 0; i < _cmdc; ++i)
{
if(!_cmdv[i]) continue;
ss << _cmdv[i] << " ";
}
fprintf(stderr, "[omnitrace][exe] Failed to create process: '%s'\n",
ss.str().c_str());
throw std::runtime_error("Failed to create process");
}
verbprintf_bare(1, "Done\n");
}
return mutatee;
}
//
//======================================================================================//
//
TIMEMORY_NOINLINE inline void
omnitrace_thread_exit(thread_t* thread, BPatch_exitType exit_type)
{
if(!thread) return;
BPatch_process* app = thread->getProcess();
if(!terminate_expr)
{
fprintf(stderr, "[omnitrace][exe] continuing execution\n");
app->continueExecution();
return;
}
switch(exit_type)
{
case ExitedNormally:
{
fprintf(stderr, "[omnitrace][exe] Thread exited normally\n");
break;
}
case ExitedViaSignal:
{
fprintf(stderr, "[omnitrace][exe] Thread terminated unexpectedly\n");
break;
}
case NoExit:
default:
{
fprintf(stderr, "[omnitrace][exe] %s invoked with NoExit\n", __FUNCTION__);
break;
}
}
// terminate_expr = nullptr;
thread->oneTimeCode(*terminate_expr);
fprintf(stderr, "[omnitrace][exe] continuing execution\n");
app->continueExecution();
}
//
//======================================================================================//
//
TIMEMORY_NOINLINE inline void
omnitrace_fork_callback(thread_t* parent, thread_t* child)
{
if(child)
{
auto* app = child->getProcess();
if(app)
{
verbprintf(4, "Stopping execution and detaching child fork...\n");
app->stopExecution();
app->detach(true);
// app->terminateExecution();
// app->continueExecution();
}
}
if(parent)
{
auto* app = parent->getProcess();
if(app)
{
verbprintf(4, "Continuing execution on parent after fork callback...\n");
app->continueExecution();
}
}
}
//
//======================================================================================//
//