Crusher hackathon updates (#164)
- improved error handling in dyninst - improved error handling in omnitrace exe - new logging facility for omnitrace exe - improved backtraces - disable concurrent kernels in rocprofiler - updates `setup-env.sh` and modulefile - set `omnitrace_ROOT` - set `HSA_TOOLS_LIB` if roctracer or rocprofiler enabled - set `ROCP_TOOL_LIB` if rocprofiler enabled - closes #163 - No longer make setting `HSA_ENABLE_INTERRUPT=0` the default - this has performance implications - this was set to workaround a bug in ROCR which caused an ioctl call in ROCm to hang when interrupted. But it was only interrupted when realtime sampling was enabled since the CPU-clock doesn't increment when waiting - This bug should be fixed in ROCm 5.3 - omnitrace no longer activates a realtime sampler by default when sampling, thus this bug is no longer encountered unless the user explicitly triggers realtime sampling
This commit is contained in:
1 parent
472e96a084
commit
90ff7188f8
34 files changed
+932
-341
No files matched your search
@@ -13,11 +13,13 @@ target_sources(
|
||||
PRIVATE ${CMAKE_CURRENT_LIST_DIR}/omnitrace.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/details.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/function_signature.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/log.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}/info.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/log.hpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/module_function.hpp)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "log.hpp"
|
||||
#include "omnitrace.hpp"
|
||||
|
||||
#include <string>
|
||||
@@ -148,6 +149,8 @@ get_func_file_line_info(module_t* module, procedure_t* func)
|
||||
{
|
||||
using address_t = Dyninst::Address;
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Getting function line info for", get_name(func));
|
||||
|
||||
auto _file_name = get_name(module);
|
||||
auto _func_name = get_name(func);
|
||||
auto _return_type = get_return_type(func);
|
||||
@@ -179,6 +182,8 @@ function_signature
|
||||
get_loop_file_line_info(module_t* module, procedure_t* func, flow_graph_t*,
|
||||
basic_loop_t* loopToInstrument)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Getting loop line info for", get_name(func));
|
||||
|
||||
auto basic_blocks = std::vector<BPatch_basicBlock*>{};
|
||||
loopToInstrument->getLoopBasicBlocksExclusive(basic_blocks);
|
||||
|
||||
@@ -271,6 +276,8 @@ get_basic_block_file_line_info(module_t* module, procedure_t* func)
|
||||
std::map<basic_block_t*, basic_block_signature> _data{};
|
||||
if(!func) return _data;
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Getting basic block line info for", get_name(func));
|
||||
|
||||
auto* _cfg = func->getCFG();
|
||||
auto _basic_blocks = std::set<BPatch_basicBlock*>{};
|
||||
_cfg->getAllBasicBlocks(_basic_blocks);
|
||||
@@ -347,6 +354,8 @@ get_basic_block_file_line_info(module_t* module, procedure_t* func)
|
||||
std::vector<statement_t>
|
||||
get_source_code(module_t* module, procedure_t* func)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Getting source code for", get_name(func));
|
||||
|
||||
std::vector<statement_t> _lines{};
|
||||
if(!module || !func) return _lines;
|
||||
auto* _cfg = func->getCFG();
|
||||
@@ -370,26 +379,6 @@ get_source_code(module_t* module, procedure_t* func)
|
||||
return _lines;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// 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
|
||||
@@ -427,11 +416,30 @@ find_function(image_t* app_image, const std::string& _name, const strset_t& _ext
|
||||
return _func;
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// Error callback routine.
|
||||
//
|
||||
void
|
||||
errorFunc(error_level_t level, int num, const char** params)
|
||||
{
|
||||
error_func_real(level, num, params);
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
void
|
||||
error_func_real(error_level_t level, int num, const char* const* params)
|
||||
{
|
||||
char line[4096];
|
||||
|
||||
const char* msg = bpatch->getEnglishErrorString(num);
|
||||
bpatch->formatErrorString(line, sizeof(line), msg, params);
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Dyninst error function called with level", level,
|
||||
":: ID# =", num, "::", line)
|
||||
.force(level < BPatchInfo);
|
||||
|
||||
if(num == 0)
|
||||
{
|
||||
// conditional reporting of warnings and informational messages
|
||||
@@ -439,34 +447,95 @@ error_func_real(error_level_t level, int num, const char* const* params)
|
||||
{
|
||||
if(level == BPatchInfo)
|
||||
{
|
||||
if(error_print > 1) printf("%s\n", params[0]);
|
||||
errprintf(2, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num,
|
||||
line, tim::log::color::end());
|
||||
}
|
||||
else
|
||||
printf("%s", params[0]);
|
||||
{
|
||||
verbprintf(0, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num,
|
||||
line, tim::log::color::end());
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
verbprintf(-1, "%s :: %i :: %s\n%s", std::to_string(level).c_str(), num, line,
|
||||
tim::log::color::end());
|
||||
// We consider some errors fatal.
|
||||
if(num == 101) exit(-1);
|
||||
if(num == 101) throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// We've a null error function when we don't want to display an error
|
||||
// Just log it
|
||||
//
|
||||
void
|
||||
error_func_fake(error_level_t level, int num, const char* const* params)
|
||||
{
|
||||
consume_parameters(level, num, params);
|
||||
// It does nothing.
|
||||
char line[4096];
|
||||
|
||||
const char* msg = bpatch->getEnglishErrorString(num);
|
||||
bpatch->formatErrorString(line, sizeof(line), msg, params);
|
||||
|
||||
// just log it
|
||||
OMNITRACE_ADD_LOG_ENTRY("Dyninst error function called with level", level,
|
||||
":: ID# =", num, "::", line)
|
||||
.force(level < BPatchInfo);
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string
|
||||
to_string(instruction_category_t _category)
|
||||
{
|
||||
using namespace Dyninst::InstructionAPI;
|
||||
switch(_category)
|
||||
{
|
||||
case c_CallInsn: return "function_call";
|
||||
case c_ReturnInsn: return "return";
|
||||
case c_BranchInsn: return "branch";
|
||||
case c_CompareInsn: return "compare";
|
||||
case c_PrefetchInsn: return "prefetch";
|
||||
case c_SysEnterInsn: return "sys_enter";
|
||||
case c_SyscallInsn: return "sys_call";
|
||||
case c_VectorInsn: return "vector";
|
||||
case c_GPUKernelExitInsn: return "gpu_kernel_exit";
|
||||
case c_NoCategory: return "no_category";
|
||||
}
|
||||
return std::string{ "unknown_category_id_" } +
|
||||
std::to_string(static_cast<int>(_category));
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(error_level_t _level)
|
||||
{
|
||||
switch(_level)
|
||||
{
|
||||
case BPatchFatal:
|
||||
{
|
||||
return JOIN("", tim::log::color::fatal(), "FatalError");
|
||||
}
|
||||
case BPatchSerious:
|
||||
{
|
||||
return JOIN("", tim::log::color::fatal(), "SeriousError");
|
||||
}
|
||||
case BPatchWarning:
|
||||
{
|
||||
return JOIN("", tim::log::color::warning(), "Warning");
|
||||
}
|
||||
case BPatchInfo:
|
||||
{
|
||||
return JOIN("", tim::log::color::info(), "Info");
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return JOIN("", tim::log::color::warning(), "UnknownErrorLevel",
|
||||
static_cast<int>(_level));
|
||||
}
|
||||
} // namespace std
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/environment.hpp>
|
||||
#include <timemory/mpl/apply.hpp>
|
||||
@@ -48,6 +50,7 @@
|
||||
#include <BPatch_snippet.h>
|
||||
#include <BPatch_statement.h>
|
||||
#include <Instruction.h>
|
||||
#include <InstructionCategories.h>
|
||||
#include <Symtab.h>
|
||||
#include <SymtabReader.h>
|
||||
#include <dyntypes.h>
|
||||
@@ -88,48 +91,49 @@ 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 statement_t = BPatch_statement;
|
||||
using basic_block_t = BPatch_basicBlock;
|
||||
using basic_loop_t = BPatch_basicBlockLoop;
|
||||
using procedure_loc_t = BPatch_procedureLocation;
|
||||
using point_t = BPatch_point;
|
||||
using object_t = BPatch_object;
|
||||
using local_var_t = BPatch_localVar;
|
||||
using sequence_t = BPatch_sequence;
|
||||
using const_expr_t = BPatch_constExpr;
|
||||
using error_level_t = BPatchErrorLevel;
|
||||
using snippet_handle_t = BPatchSnippetHandle;
|
||||
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;
|
||||
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 statement_t = BPatch_statement;
|
||||
using basic_block_t = BPatch_basicBlock;
|
||||
using basic_loop_t = BPatch_basicBlockLoop;
|
||||
using procedure_loc_t = BPatch_procedureLocation;
|
||||
using point_t = BPatch_point;
|
||||
using object_t = BPatch_object;
|
||||
using local_var_t = BPatch_localVar;
|
||||
using sequence_t = BPatch_sequence;
|
||||
using const_expr_t = BPatch_constExpr;
|
||||
using error_level_t = BPatchErrorLevel;
|
||||
using snippet_handle_t = BPatchSnippetHandle;
|
||||
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;
|
||||
using instruction_category_t = Dyninst::InstructionAPI::InsnCategory;
|
||||
|
||||
void
|
||||
omnitrace_prefork_callback(thread_t* parent, thread_t* child);
|
||||
@@ -172,6 +176,7 @@ extern bool werror;
|
||||
extern bool debug_print;
|
||||
extern bool instr_print;
|
||||
extern int verbose_level;
|
||||
extern int num_log_entries;
|
||||
//
|
||||
// instrumentation settings
|
||||
//
|
||||
@@ -204,12 +209,18 @@ extern regexvec_t file_exclude;
|
||||
extern regexvec_t file_restrict;
|
||||
extern regexvec_t func_restrict;
|
||||
extern CodeCoverageMode coverage_mode;
|
||||
|
||||
// logging
|
||||
extern std::unique_ptr<std::ofstream> log_ofs;
|
||||
//
|
||||
//======================================================================================//
|
||||
|
||||
// control debug printf statements
|
||||
#define errprintf(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
OMNITRACE_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(werror || LEVEL < 0) \
|
||||
{ \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
@@ -229,6 +240,9 @@ extern CodeCoverageMode coverage_mode;
|
||||
// control verbose printf statements
|
||||
#define verbprintf(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
OMNITRACE_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(debug_print || verbose_level >= LEVEL) \
|
||||
fprintf(stdout, "[omnitrace][exe] " __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
@@ -236,6 +250,9 @@ extern CodeCoverageMode coverage_mode;
|
||||
|
||||
#define verbprintf_bare(LEVEL, ...) \
|
||||
{ \
|
||||
char _logmsgbuff[FUNCNAMELEN]; \
|
||||
snprintf(_logmsgbuff, FUNCNAMELEN, __VA_ARGS__); \
|
||||
OMNITRACE_ADD_LOG_ENTRY(_logmsgbuff); \
|
||||
if(debug_print || verbose_level >= LEVEL) fprintf(stdout, __VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
}
|
||||
@@ -289,9 +306,6 @@ bool
|
||||
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
|
||||
basic_block_t* basicBlock, bool allow_traps = instr_traps);
|
||||
|
||||
void
|
||||
errorFunc(error_level_t level, int num, const char** params);
|
||||
|
||||
procedure_t*
|
||||
find_function(image_t* appImage, const string_t& functionName, const strset_t& = {});
|
||||
|
||||
@@ -306,3 +320,9 @@ get_name(procedure_t* _module);
|
||||
|
||||
std::string_view
|
||||
get_name(module_t* _module);
|
||||
|
||||
namespace std
|
||||
{
|
||||
std::string to_string(instruction_category_t);
|
||||
std::string to_string(error_level_t);
|
||||
} // namespace std
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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 "log.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
namespace color = tim::log::color;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<log_entry> log_entries = {};
|
||||
|
||||
auto
|
||||
get_color_regex(std::string _v)
|
||||
{
|
||||
auto _p = _v.find("[");
|
||||
if(_p != std::string::npos) _v.insert(_p, "\\");
|
||||
return JOIN("", "\\", _v);
|
||||
}
|
||||
|
||||
auto _color_regex = std::regex{ JOIN("", "(", get_color_regex(tim::log::color::info()),
|
||||
"|", get_color_regex(tim::log::color::source()), "|",
|
||||
get_color_regex(tim::log::color::warning()), "|",
|
||||
get_color_regex(tim::log::color::fatal()), "|",
|
||||
get_color_regex(tim::log::color::end()), ")"),
|
||||
std::regex_constants::optimize };
|
||||
} // namespace
|
||||
|
||||
log_entry::log_entry(std::string _msg)
|
||||
: m_message{ std::move(_msg) }
|
||||
, m_backtrace{ tim::get_unw_stack<4, 1>() }
|
||||
{
|
||||
if(log_ofs) *log_ofs << as_string("", "", "") << "\n";
|
||||
}
|
||||
|
||||
log_entry::log_entry(source_location _loc, std::string _msg)
|
||||
: m_location{ _loc }
|
||||
, m_message{ std::move(_msg) }
|
||||
, m_backtrace{ tim::get_unw_stack<4, 1>() }
|
||||
{
|
||||
if(log_ofs) *log_ofs << as_string("", "", "") << "\n";
|
||||
}
|
||||
|
||||
std::string
|
||||
log_entry::as_string(const char* _color, const char* _src, const char* _end) const
|
||||
{
|
||||
std::stringstream _ss;
|
||||
if(m_location.function && m_location.file)
|
||||
{
|
||||
_ss << "[" << _src << m_location.file << ":" << m_location.line << _end << "]["
|
||||
<< _src << m_location.function << _end << "]";
|
||||
}
|
||||
|
||||
bool _remove_color = (strlen(_color) + strlen(_src) + strlen(_end) == 0);
|
||||
|
||||
_ss << " " << _color << std::regex_replace(m_message, std::regex{ "\n" }, " ... ")
|
||||
<< _end;
|
||||
|
||||
return (_remove_color) ? std::regex_replace(_ss.str(), _color_regex, "") : _ss.str();
|
||||
}
|
||||
|
||||
log_entry&
|
||||
log_entry::add_log_entry(log_entry&& _v)
|
||||
{
|
||||
return log_entries.emplace_back(std::move(_v));
|
||||
}
|
||||
|
||||
void
|
||||
print_log_entries(std::ostream& _os, int64_t _count,
|
||||
std::function<bool(const log_entry&)> _condition, const char* _color,
|
||||
bool _color_entries)
|
||||
{
|
||||
size_t i0 = (_count < 0) ? 0 : std::max<int64_t>(log_entries.size() - _count, 0);
|
||||
size_t _w = std::log10(log_entries.size()) + 1;
|
||||
|
||||
if(dynamic_cast<std::ofstream*>(&_os) ||
|
||||
(&_os != &std::cout && &_os != &std::cerr && &_os != &std::clog))
|
||||
{
|
||||
_color = "";
|
||||
_color_entries = false;
|
||||
}
|
||||
|
||||
const char* _end =
|
||||
(strlen(_color) > 0 || _color_entries) ? tim::log::color::end() : "";
|
||||
|
||||
// the requested number of log entries
|
||||
for(size_t i = i0; i < log_entries.size(); ++i)
|
||||
{
|
||||
auto& itr = log_entries.at(i);
|
||||
|
||||
if(!_condition || _condition(itr))
|
||||
{
|
||||
_os << "[" << _color << std::setw(_w) << i << "/" << log_entries.size()
|
||||
<< _end << "]"
|
||||
<< ((_color_entries) ? itr.as_string() : itr.as_string("", "", ""))
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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/log/color.hpp>
|
||||
#include <timemory/utility/backtrace.hpp>
|
||||
#include <timemory/utility/join.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#if !defined(JOIN)
|
||||
# define JOIN(...) ::timemory::join::join(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
struct log_entry;
|
||||
|
||||
void
|
||||
print_log_entries(std::ostream& = std::cerr, int64_t _count = 10,
|
||||
std::function<bool(const log_entry&)> _cond = {},
|
||||
const char* _color = tim::log::color::warning(),
|
||||
bool _color_entries = true);
|
||||
|
||||
struct log_entry
|
||||
{
|
||||
struct source_location
|
||||
{
|
||||
const char* function = nullptr;
|
||||
const char* file = nullptr;
|
||||
int line = 0;
|
||||
};
|
||||
|
||||
log_entry(std::string _msg);
|
||||
log_entry(source_location _loc, std::string _msg);
|
||||
|
||||
std::string as_string(const char* _color = tim::log::color::info(),
|
||||
const char* _src = tim::log::color::source(),
|
||||
const char* _end = tim::log::color::end()) const;
|
||||
|
||||
static log_entry& add_log_entry(log_entry&&);
|
||||
|
||||
log_entry& force(bool _v = true)
|
||||
{
|
||||
m_forced = _v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool forced() const { return m_forced; }
|
||||
|
||||
private:
|
||||
bool m_forced = false; // if should always be displayed
|
||||
source_location m_location = {};
|
||||
std::string m_message = {};
|
||||
tim::unwind::stack<4> m_backtrace = {};
|
||||
|
||||
friend void print_log_entries(std::ostream&, int64_t,
|
||||
std::function<bool(const log_entry&)>, const char*,
|
||||
bool);
|
||||
};
|
||||
|
||||
#define OMNITRACE_ADD_LOG_ENTRY(...) \
|
||||
log_entry::add_log_entry( \
|
||||
{ log_entry::source_location{ __FUNCTION__, __FILE__, __LINE__ }, \
|
||||
timemory::join::join(' ', __VA_ARGS__) })
|
||||
|
||||
#define OMNITRACE_ADD_DETAILED_LOG_ENTRY(DELIM, ...) \
|
||||
log_entry::add_log_entry( \
|
||||
{ log_entry::source_location{ __FUNCTION__, __FILE__, __LINE__ }, \
|
||||
timemory::join::join(__VA_ARGS__) })
|
||||
@@ -21,7 +21,9 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "module_function.hpp"
|
||||
#include "InstructionCategories.h"
|
||||
#include "fwd.hpp"
|
||||
#include "log.hpp"
|
||||
#include "omnitrace.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
@@ -58,6 +60,8 @@ module_function::module_function(module_t* mod, procedure_t* proc)
|
||||
, module_name{ get_name(module) }
|
||||
, function_name{ get_name(function) }
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Adding function", function_name, "from module", module_name);
|
||||
|
||||
if(!function->isInstrumentable())
|
||||
{
|
||||
verbprintf(1,
|
||||
@@ -75,6 +79,10 @@ module_function::module_function(module_t* mod, procedure_t* proc)
|
||||
|
||||
signature = get_func_file_line_info(module, function);
|
||||
|
||||
// make sure all exist
|
||||
for(int i = 0; i <= instruction_category_t::c_NoCategory; ++i)
|
||||
instruction_types[static_cast<instruction_category_t>(i)] = 0;
|
||||
|
||||
if(function->isInstrumentable())
|
||||
{
|
||||
// this information is potentially not available and
|
||||
@@ -88,13 +96,39 @@ module_function::module_function(module_t* mod, procedure_t* proc)
|
||||
}
|
||||
|
||||
instructions.reserve(basic_blocks.size());
|
||||
size_t _n = 0;
|
||||
for(const auto& itr : basic_blocks)
|
||||
{
|
||||
std::vector<instruction_t> _instructions{};
|
||||
itr->getInstructions(_instructions);
|
||||
num_instructions += _instructions.size();
|
||||
if(debug_print || verbose_level > 3 || instr_print)
|
||||
instructions.emplace_back(std::move(_instructions));
|
||||
std::vector<std::pair<instruction_t, address_t>> _instructions{};
|
||||
try
|
||||
{
|
||||
if(itr->getInstructions(_instructions))
|
||||
{
|
||||
auto _num_instr = _instructions.size();
|
||||
num_instructions += _num_instr;
|
||||
for(auto&& iitr : _instructions)
|
||||
{
|
||||
instruction_types[iitr.first.getCategory()] += 1;
|
||||
}
|
||||
// num_instructions += _instructions.size();
|
||||
if(debug_print || verbose_level > 3 || instr_print)
|
||||
instructions.emplace_back(std::move(_instructions));
|
||||
}
|
||||
else
|
||||
{
|
||||
// on average, the number of instructions is address range / 4
|
||||
auto _num_instr = (itr->getEndAddress() - itr->getStartAddress()) / 4;
|
||||
verbprintf(2,
|
||||
"No instructions found for basic block %zu in %s. "
|
||||
"Approximating with %lu...\n",
|
||||
_n, function_name.c_str(), _num_instr);
|
||||
num_instructions += _num_instr;
|
||||
}
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
errprintf(1, "Dyninst error: %s\n", _e.what());
|
||||
}
|
||||
++_n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,11 @@
|
||||
|
||||
struct module_function
|
||||
{
|
||||
using width_t = std::array<size_t, 4>;
|
||||
using address_t = Dyninst::Address;
|
||||
using width_t = std::array<size_t, 4>;
|
||||
using address_t = Dyninst::Address;
|
||||
using instr_addr_pair_t = std::pair<instruction_t, address_t>;
|
||||
using str_msg_t = std::tuple<int, string_t, string_t, string_t, string_t>;
|
||||
using str_msg_vec_t = std::vector<str_msg_t>;
|
||||
|
||||
static constexpr size_t absolute_max_width = 80;
|
||||
static width_t& get_width();
|
||||
@@ -86,21 +89,19 @@ struct module_function
|
||||
bool is_address_range_constrained() const; // checks address range constraint
|
||||
bool is_num_instructions_constrained() const; // check # instructions constraint
|
||||
|
||||
size_t start_address = 0;
|
||||
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 = {};
|
||||
std::vector<std::vector<instruction_t>> instructions = {};
|
||||
|
||||
using str_msg_t = std::tuple<int, string_t, string_t, string_t, string_t>;
|
||||
using str_msg_vec_t = std::vector<str_msg_t>;
|
||||
size_t start_address = 0;
|
||||
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 = {};
|
||||
std::map<instruction_category_t, int64_t> instruction_types = {};
|
||||
std::vector<std::vector<instr_addr_pair_t>> instructions = {};
|
||||
|
||||
mutable str_msg_vec_t messages = {};
|
||||
|
||||
@@ -211,21 +212,30 @@ module_function::serialize(ArchiveT& ar, const unsigned)
|
||||
cereal::make_nvp("is_loop_num_instructions_constrained",
|
||||
is_loop_num_instructions_constrained()));
|
||||
ar.finishNode();
|
||||
ar.setNextName("instruction_breakdown");
|
||||
ar.startNode();
|
||||
for(auto itr : instruction_types)
|
||||
ar(cereal::make_nvp(std::to_string(itr.first).c_str(), itr.second));
|
||||
ar.finishNode();
|
||||
// instructions can inflate JSON size so only output when verbosity is increased
|
||||
// above default
|
||||
if(debug_print || verbose_level > 3 || instr_print)
|
||||
{
|
||||
std::vector<std::vector<std::string>> _instructions{};
|
||||
_instructions.reserve(instructions.size());
|
||||
ar.setNextName("instructions");
|
||||
ar.startNode();
|
||||
ar.makeArray();
|
||||
for(auto&& itr : instructions)
|
||||
{
|
||||
std::vector<std::string> _subinstr{};
|
||||
_subinstr.reserve(itr.size());
|
||||
ar.startNode();
|
||||
for(auto&& iitr : itr)
|
||||
_subinstr.emplace_back(iitr.format());
|
||||
_instructions.emplace_back(std::move(_subinstr));
|
||||
{
|
||||
std::stringstream _addr{};
|
||||
_addr << "0x" << std::hex << iitr.second;
|
||||
ar(cereal::make_nvp(_addr.str().c_str(), iitr.first.format()));
|
||||
}
|
||||
ar.finishNode();
|
||||
}
|
||||
ar(cereal::make_nvp("instructions", _instructions));
|
||||
ar.finishNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,18 @@
|
||||
|
||||
#include "omnitrace.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <timemory/backends/process.hpp>
|
||||
#include <timemory/config.hpp>
|
||||
#include <timemory/hash.hpp>
|
||||
#include <timemory/log/macros.hpp>
|
||||
#include <timemory/manager.hpp>
|
||||
#include <timemory/sampling/signals.hpp>
|
||||
#include <timemory/settings.hpp>
|
||||
#include <timemory/utility/console.hpp>
|
||||
#include <timemory/utility/demangle.hpp>
|
||||
#include <timemory/utility/signals.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -78,6 +83,7 @@ bool instr_print = false;
|
||||
bool simulate = false;
|
||||
bool include_uninstr = false;
|
||||
int verbose_level = tim::get_env<int>("OMNITRACE_VERBOSE_INSTRUMENT", 0);
|
||||
int num_log_entries = tim::get_env<int>("OMNITRACE_LOG_COUNT", 20);
|
||||
string_t main_fname = "main";
|
||||
string_t argv0 = {};
|
||||
string_t cmdv0 = {};
|
||||
@@ -104,6 +110,8 @@ regexvec_t file_restrict = {};
|
||||
regexvec_t func_restrict = {};
|
||||
CodeCoverageMode coverage_mode = CODECOV_NONE;
|
||||
|
||||
std::unique_ptr<std::ofstream> log_ofs = {};
|
||||
|
||||
namespace
|
||||
{
|
||||
bool binary_rewrite = false;
|
||||
@@ -166,6 +174,8 @@ void
|
||||
find_dyn_api_rt();
|
||||
} // namespace
|
||||
|
||||
namespace process = tim::process;
|
||||
|
||||
//======================================================================================//
|
||||
//
|
||||
// entry point
|
||||
@@ -175,11 +185,54 @@ find_dyn_api_rt();
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
{
|
||||
using signal_settings = tim::signal_settings;
|
||||
using sys_signal = tim::sys_signal;
|
||||
|
||||
// default signals to catch
|
||||
for(const auto& itr :
|
||||
{ sys_signal::Interrupt, sys_signal::FPE, sys_signal::Stop, sys_signal::Quit,
|
||||
sys_signal::Illegal, sys_signal::Abort, sys_signal::Bus,
|
||||
sys_signal::SegFault, sys_signal::FileSize, sys_signal::CPUtime })
|
||||
signal_settings::enable(itr);
|
||||
|
||||
auto _exit_action = [](int nsig) {
|
||||
TIMEMORY_PRINTF_FATAL(
|
||||
stderr, "omnitrace exited with signal %i :: %s\n", nsig,
|
||||
signal_settings::str(static_cast<sys_signal>(nsig)).c_str());
|
||||
|
||||
print_log_entries(std::cerr, num_log_entries);
|
||||
|
||||
std::cerr << "\n[omnitrace][exe] Potentially important log entries\n\n";
|
||||
|
||||
print_log_entries(std::cerr, -1, [](const auto& _v) { return _v.forced(); });
|
||||
|
||||
TIMEMORY_PRINTF_FATAL(stderr, "\n");
|
||||
TIMEMORY_PRINTF_FATAL(
|
||||
stderr,
|
||||
"These were the last %i log entries from omnitrace. You can control the "
|
||||
"number of log entries via the '--log <N>' option or OMNITRACE_LOG_COUNT "
|
||||
"env variable.\n");
|
||||
|
||||
if(log_ofs) log_ofs->close();
|
||||
log_ofs.reset();
|
||||
|
||||
kill(process::get_id(), nsig);
|
||||
};
|
||||
|
||||
signal_settings::set_exit_action(_exit_action);
|
||||
signal_settings::check_environment();
|
||||
tim::enable_signal_detection(signal_settings::get_enabled());
|
||||
}
|
||||
|
||||
argv0 = argv[0];
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY(argv[0]);
|
||||
|
||||
address_space_t* addr_space = nullptr;
|
||||
string_t mutname = {};
|
||||
string_t outfile = {};
|
||||
string_t logfile = {};
|
||||
std::vector<string_t> inputlib = { "libomnitrace-dl" };
|
||||
std::vector<string_t> libname = {};
|
||||
std::vector<string_t> sharedlibname = {};
|
||||
@@ -298,6 +351,21 @@ main(int argc, char** argv)
|
||||
debug_print = p.get<bool>("debug");
|
||||
if(debug_print && !p.exists("verbose")) verbose_level = 256;
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--log" }, "Number of log entries to display after an error. Any "
|
||||
"value < 0 will emit the entire log")
|
||||
.count(1)
|
||||
.action([](parser_t& p) { num_log_entries = p.get<int>("log"); });
|
||||
parser
|
||||
.add_argument({ "--log-file" },
|
||||
"Write the log out the specified file during the run")
|
||||
.count(1)
|
||||
.action([&logfile](parser_t& p) {
|
||||
auto _oname = p.get<std::string>("log-file");
|
||||
auto _cfg = tim::settings::compose_filename_config{};
|
||||
_cfg.subdirectory = "instrumentation";
|
||||
logfile = tim::settings::compose_output_filename(_oname, "log", _cfg);
|
||||
});
|
||||
parser
|
||||
.add_argument({ "--simulate" },
|
||||
"Exit after outputting diagnostic "
|
||||
@@ -971,6 +1039,17 @@ main(int argc, char** argv)
|
||||
tim::timemory_init(_cmdc, _cmdv, "omnitrace-");
|
||||
}
|
||||
|
||||
if(!logfile.empty())
|
||||
{
|
||||
log_ofs = std::make_unique<std::ofstream>();
|
||||
verbprintf_bare(0, "%s", ::tim::log::color::source());
|
||||
verbprintf(0, "Opening '%s' for log output... ", logfile.c_str());
|
||||
if(!tim::filepath::open(*log_ofs, logfile))
|
||||
throw std::runtime_error(JOIN(" ", "Error opening log output file", logfile));
|
||||
verbprintf_bare(0, "Done\n%s", ::tim::log::color::end());
|
||||
print_log_entries(*log_ofs, -1, {}, "", false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
//
|
||||
// REGEX OPTIONS
|
||||
@@ -980,6 +1059,9 @@ main(int argc, char** argv)
|
||||
{
|
||||
// Helper function for adding regex expressions
|
||||
auto add_regex = [](auto& regex_array, const string_t& regex_expr) {
|
||||
OMNITRACE_ADD_DETAILED_LOG_ENTRY("", "Adding regular expression \"",
|
||||
regex_expr, "\" to regex_array@",
|
||||
®ex_array);
|
||||
if(!regex_expr.empty())
|
||||
regex_array.emplace_back(std::regex(regex_expr, regex_opts));
|
||||
};
|
||||
@@ -1099,26 +1181,6 @@ main(int argc, char** argv)
|
||||
//
|
||||
//----------------------------------------------------------------------------------//
|
||||
|
||||
// for runtime instrumentation, we need to set this before the process gets created
|
||||
if(!binary_rewrite)
|
||||
{
|
||||
auto _hsa_val = tim::get_env<int>("HSA_ENABLE_INTERRUPT", 1, false);
|
||||
tim::set_env("HSA_ENABLE_INTERRUPT", "0", 0);
|
||||
if(_pid >= 0 && _hsa_val != 0)
|
||||
{
|
||||
verbprintf(0, "#-------------------------------------------------------"
|
||||
"-------------------------------------------#\n");
|
||||
verbprintf(0, "\n");
|
||||
verbprintf(0, "WARNING! Sampling may result in ioctl() deadlock within "
|
||||
"the ROCR runtime.\n");
|
||||
verbprintf(0, "To avoid this, set HSA_ENABLE_INTERRUPT=0 in the environment "
|
||||
"before starting your ROCm/HIP application\n");
|
||||
verbprintf(0, "\n");
|
||||
verbprintf(0, "#-------------------------------------------------------"
|
||||
"-------------------------------------------#\n");
|
||||
}
|
||||
}
|
||||
|
||||
addr_space =
|
||||
omnitrace_get_address_space(bpatch, _cmdc, _cmdv, binary_rewrite, _pid, mutname);
|
||||
|
||||
@@ -1166,10 +1228,14 @@ main(int argc, char** argv)
|
||||
};
|
||||
|
||||
auto _add_overlapping = [](module_t* mitr, procedure_t* pitr) {
|
||||
OMNITRACE_ADD_LOG_ENTRY("Checking if procedure", get_name(pitr), "in module",
|
||||
get_name(mitr), "is overlapping");
|
||||
if(!pitr->isInstrumentable()) return;
|
||||
std::vector<procedure_t*> _overlapping{};
|
||||
if(pitr->findOverlapping(_overlapping))
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Adding overlapping procedure", get_name(pitr),
|
||||
"and module", get_name(mitr));
|
||||
_insert_module_function(overlapping_module_functions,
|
||||
module_function{ mitr, pitr });
|
||||
for(auto* oitr : _overlapping)
|
||||
@@ -1289,6 +1355,8 @@ main(int argc, char** argv)
|
||||
|
||||
is_static_exe = addr_space->isStaticExecutable();
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("address space is", (is_static_exe) ? "" : "not",
|
||||
"a static executable");
|
||||
if(binary_rewrite)
|
||||
app_binary = static_cast<BPatch_binaryEdit*>(addr_space);
|
||||
else
|
||||
@@ -1296,6 +1364,8 @@ main(int argc, char** argv)
|
||||
|
||||
is_attached = (_pid >= 0 && app_thread != nullptr);
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("address space is attached:", is_attached);
|
||||
|
||||
if(!app_binary && !app_thread)
|
||||
{
|
||||
errprintf(-1, "No application thread or binary! nullptr to BPatch_binaryEdit* "
|
||||
@@ -1314,21 +1384,26 @@ main(int argc, char** argv)
|
||||
string_t _tried_libs;
|
||||
for(auto _libname : _libnames)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Getting the absolute lib filepath to", _libname);
|
||||
_libname = get_absolute_lib_filepath(_libname);
|
||||
_tried_libs += string_t("|") + _libname;
|
||||
verbprintf(1, "loading library: '%s'...\n", _libname.c_str());
|
||||
result = (addr_space->loadLibrary(_libname.c_str()) != nullptr);
|
||||
verbprintf(2, "loadLibrary(%s) result = %s\n", _libname.c_str(),
|
||||
(result) ? "success" : "failure");
|
||||
if(result) break;
|
||||
if(result)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Using library:", _libname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!result)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Error: 'loadLibrary(%s)' failed.\nPlease ensure that the "
|
||||
"library directory is in LD_LIBRARY_PATH environment variable "
|
||||
"or absolute path is provided\n",
|
||||
_tried_libs.substr(1).c_str());
|
||||
errprintf(-127,
|
||||
"Error: 'loadLibrary(%s)' failed.\nPlease ensure that the "
|
||||
"library directory is in LD_LIBRARY_PATH environment variable "
|
||||
"or absolute path is provided\n",
|
||||
_tried_libs.substr(1).c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
};
|
||||
@@ -1347,6 +1422,7 @@ main(int argc, char** argv)
|
||||
};
|
||||
for(auto& lname : lnames)
|
||||
lname = _get_library_ext(lname);
|
||||
OMNITRACE_ADD_LOG_ENTRY("Using library:", lnames);
|
||||
return lnames;
|
||||
};
|
||||
|
||||
@@ -1623,10 +1699,6 @@ main(int argc, char** argv)
|
||||
for(auto&& itr : env_config_variables)
|
||||
env_vars.emplace_back(itr);
|
||||
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MODE", instr_mode));
|
||||
env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_ENABLE_INTERRUPT", "0"));
|
||||
#if defined(OMNITRACE_USE_ROCTRACER) && OMNITRACE_USE_ROCTRACER > 0
|
||||
env_vars.emplace_back(TIMEMORY_JOIN('=', "HSA_TOOLS_LIB", _libname));
|
||||
#endif
|
||||
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MPI_INIT", "OFF"));
|
||||
env_vars.emplace_back(TIMEMORY_JOIN('=', "OMNITRACE_MPI_FINALIZE", "OFF"));
|
||||
env_vars.emplace_back(
|
||||
@@ -1756,26 +1828,43 @@ main(int argc, char** argv)
|
||||
if(main_entr_points)
|
||||
{
|
||||
verbprintf(1, "Adding main entry snippets...\n");
|
||||
addr_space->insertSnippet(_init_sequence, *main_entr_points,
|
||||
BPatch_callBefore, BPatch_firstSnippet);
|
||||
addr_space->insertSnippet(_init_sequence, *main_entr_points);
|
||||
// insert_instr(addr_space, *main_entr_points, _init_sequence, BPatch_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto* itr : _objs)
|
||||
itr->insertInitCallback(_init_sequence);
|
||||
{
|
||||
try
|
||||
{
|
||||
itr->insertInitCallback(_init_sequence);
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
errprintf(0, "Dyninst error inserting init callback: %s\n",
|
||||
_e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(main_exit_points)
|
||||
{
|
||||
verbprintf(1, "Adding main exit snippets...\n");
|
||||
addr_space->insertSnippet(_fini_sequence, *main_exit_points, BPatch_callAfter,
|
||||
BPatch_firstSnippet);
|
||||
// insert_instr(addr_space, *main_exit_points, _fini_sequence, BPatch_exit);
|
||||
addr_space->insertSnippet(_fini_sequence, *main_exit_points);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto* itr : _objs)
|
||||
itr->insertFiniCallback(_fini_sequence);
|
||||
{
|
||||
try
|
||||
{
|
||||
itr->insertFiniCallback(_fini_sequence);
|
||||
} catch(std::runtime_error& _e)
|
||||
{
|
||||
errprintf(0, "Dyninst error inserting fini callback: %s\n", _e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------//
|
||||
@@ -2173,6 +2262,12 @@ main(int argc, char** argv)
|
||||
|
||||
verbprintf(0, "End of omnitrace\n");
|
||||
verbprintf(1, "Exit code: %i\n", code);
|
||||
|
||||
if(log_ofs)
|
||||
{
|
||||
verbprintf(2, "Closing log file...\n");
|
||||
log_ofs->close();
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "function_signature.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "info.hpp"
|
||||
#include "log.hpp"
|
||||
#include "module_function.hpp"
|
||||
|
||||
#include <timemory/utility/filepath.hpp>
|
||||
@@ -199,8 +200,7 @@ omnitrace_get_address_space(patch_pointer_t& _bpatch, int _cmdc, char** _cmdv,
|
||||
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());
|
||||
verbprintf(-1, "Failed to open binary '%s'\n", _name.c_str());
|
||||
throw std::runtime_error("Failed to open binary");
|
||||
}
|
||||
verbprintf_bare(1, "Done\n");
|
||||
@@ -213,27 +213,28 @@ omnitrace_get_address_space(patch_pointer_t& _bpatch, int _cmdc, char** _cmdv,
|
||||
mutatee = _bpatch->processAttach(_cmdv0, _pid);
|
||||
if(!mutatee)
|
||||
{
|
||||
fprintf(stderr, "[omnitrace][exe] Failed to connect to process %i\n",
|
||||
(int) _pid);
|
||||
verbprintf(-1, "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]);
|
||||
std::stringstream ss;
|
||||
for(int i = 0; i < _cmdc; ++i)
|
||||
{
|
||||
if(!_cmdv || !_cmdv[i]) continue;
|
||||
ss << " " << _cmdv[i];
|
||||
}
|
||||
auto _cmd_msg = ss.str();
|
||||
if(_cmd_msg.length() > 1) _cmd_msg = _cmd_msg.substr(1);
|
||||
|
||||
verbprintf(1, "Creating process '%s'... ", _cmd_msg.c_str());
|
||||
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());
|
||||
verbprintf(-1, "Failed to create process: '%s'\n", _cmd_msg.c_str());
|
||||
throw std::runtime_error("Failed to create process");
|
||||
}
|
||||
verbprintf_bare(1, "Done\n");
|
||||
@@ -249,6 +250,8 @@ omnitrace_thread_exit(thread_t* thread, BPatch_exitType exit_type)
|
||||
{
|
||||
if(!thread) return;
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Executing the thread callback");
|
||||
|
||||
BPatch_process* app = thread->getProcess();
|
||||
|
||||
if(!terminate_expr)
|
||||
@@ -290,6 +293,8 @@ omnitrace_thread_exit(thread_t* thread, BPatch_exitType exit_type)
|
||||
TIMEMORY_NOINLINE inline void
|
||||
omnitrace_fork_callback(thread_t* parent, thread_t* child)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Executing the fork callback");
|
||||
|
||||
if(child)
|
||||
{
|
||||
auto* app = child->getProcess();
|
||||
@@ -320,10 +325,20 @@ omnitrace_fork_callback(thread_t* parent, thread_t* child)
|
||||
template <typename Tp>
|
||||
bool
|
||||
insert_instr(address_space_t* mutatee, const bpvector_t<point_t*>& _points, Tp traceFunc,
|
||||
procedure_loc_t traceLoc, bool allow_traps)
|
||||
procedure_loc_t, bool allow_traps)
|
||||
{
|
||||
if(!traceFunc || _points.empty()) return false;
|
||||
|
||||
auto _names = [&_points]() {
|
||||
std::set<std::string> _v{};
|
||||
for(const auto& itr : _points)
|
||||
if(itr && itr->getFunction()) _v.emplace(get_name(itr->getFunction()));
|
||||
return _v;
|
||||
}();
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Inserting", _points.size(),
|
||||
"instrumentation points into function(s)", _names);
|
||||
|
||||
auto _trace = traceFunc.get();
|
||||
auto _traps = std::set<point_t*>{};
|
||||
if(!allow_traps)
|
||||
@@ -334,18 +349,20 @@ insert_instr(address_space_t* mutatee, const bpvector_t<point_t*>& _points, Tp t
|
||||
}
|
||||
}
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Found", _traps.size(),
|
||||
"instrumentation points using traps in function(s)", _names);
|
||||
|
||||
size_t _n = 0;
|
||||
for(const auto& itr : _points)
|
||||
{
|
||||
if(!itr || _traps.count(itr) > 0)
|
||||
continue;
|
||||
else if(traceLoc == BPatch_entry)
|
||||
mutatee->insertSnippet(*_trace, *itr, BPatch_callBefore, BPatch_firstSnippet);
|
||||
else
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
if(!itr || _traps.count(itr) > 0) continue;
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
++_n;
|
||||
}
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Inserted", _n, "instrumentation points in function(s)",
|
||||
_names);
|
||||
|
||||
return (_n > 0);
|
||||
}
|
||||
//
|
||||
@@ -364,6 +381,9 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
bpvector_t<point_t*>* _points = nullptr;
|
||||
auto _trace = traceFunc.get();
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Searching for loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
if(!cfGraph) funcToInstr->getCFG();
|
||||
if(cfGraph && loopToInstrument)
|
||||
{
|
||||
@@ -380,6 +400,10 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
if(_points == nullptr) return false;
|
||||
if(_points->empty()) return false;
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Inserting max of", _points->size(),
|
||||
"loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
std::set<point_t*> _traps{};
|
||||
if(!allow_traps)
|
||||
{
|
||||
@@ -389,18 +413,21 @@ insert_instr(address_space_t* mutatee, procedure_t* funcToInstr, Tp traceFunc,
|
||||
}
|
||||
}
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Found", _traps.size(),
|
||||
"loop instrumentation points using traps in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
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
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
if(!itr || _traps.count(itr) > 0) continue;
|
||||
mutatee->insertSnippet(*_trace, *itr);
|
||||
++_n;
|
||||
}
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY("Inserted", _n, "loop instrumentation points in function",
|
||||
get_name(funcToInstr));
|
||||
|
||||
return (_n > 0);
|
||||
}
|
||||
//
|
||||
@@ -412,9 +439,15 @@ bool
|
||||
insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
|
||||
basic_block_t* basicBlock, bool allow_traps)
|
||||
{
|
||||
if(!basicBlock) return false;
|
||||
|
||||
point_t* _point = nullptr;
|
||||
auto _trace = traceFunc.get();
|
||||
|
||||
OMNITRACE_ADD_LOG_ENTRY(
|
||||
"Searching for basic-block entry and exit instrumentation points ::",
|
||||
*basicBlock);
|
||||
|
||||
basic_block_t* _bb = basicBlock;
|
||||
switch(traceLoc)
|
||||
{
|
||||
@@ -426,15 +459,23 @@ insert_instr(address_space_t* mutatee, Tp traceFunc, procedure_loc_t traceLoc,
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_point == nullptr) return false;
|
||||
if(_point == nullptr)
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("No instrumentation points were found in basic-block ",
|
||||
*basicBlock);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!allow_traps && _point->usesTrap_NP()) return false;
|
||||
if(!allow_traps && _point->usesTrap_NP())
|
||||
{
|
||||
OMNITRACE_ADD_LOG_ENTRY("Basic-block", *basicBlock,
|
||||
"uses traps and traps are disallowed");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(traceLoc)
|
||||
{
|
||||
case BPatch_entry:
|
||||
return (mutatee->insertSnippet(*_trace, *_point, BPatch_callBefore,
|
||||
BPatch_firstSnippet) != nullptr);
|
||||
case BPatch_exit: return (mutatee->insertSnippet(*_trace, *_point) != nullptr);
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ function(OMNITRACE_ADD_BIN_TEST)
|
||||
endif()
|
||||
|
||||
# common
|
||||
list(APPEND TEST_ENVIRONMENT "OMNITRACE_CI=ON"
|
||||
list(APPEND TEST_ENVIRONMENT "OMNITRACE_CI=ON" "OMNITRACE_CONFIG_FILE="
|
||||
"OMNITRACE_OUTPUT_PATH=omnitrace-tests-output")
|
||||
# copy for inverse
|
||||
set(TEST_ENVIRONMENT_INV "${TEST_ENVIRONMENT}")
|
||||
@@ -178,7 +178,7 @@ omnitrace_add_bin_test(
|
||||
TIMEOUT 60
|
||||
PASS_REGEX
|
||||
".*available.json.*available.txt.*available.xml.*excluded.json.*excluded.txt.*excluded.xml.*instrumented.json.*instrumented.txt.*instrumented.xml.*overlapping.json.*overlapping.txt.*overlapping.xml.*"
|
||||
)
|
||||
FAIL_REGEX "No such file or directory|not found")
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
ADD_INVERSE
|
||||
@@ -191,6 +191,23 @@ omnitrace_add_bin_test(
|
||||
"\\\[omnitrace\\\]\\\[exe\\\] Runtime instrumentation is not possible!(.*)\n(.*)\\\[omnitrace\\\]\\\[exe\\\] Switching to binary rewrite mode and assuming '--simulate --all-functions'"
|
||||
)
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-exe-write-log
|
||||
TARGET omnitrace-exe
|
||||
ARGS --print-instrumented functions -v 1 --log-file user.log -- ls
|
||||
LABELS "log"
|
||||
TIMEOUT 120
|
||||
PASS_REGEX "Opening .*/instrumentation/user.log")
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-exe-write-log-check
|
||||
DEPENDS omnitrace-exe-write-log
|
||||
COMMAND ls omnitrace-tests-output/omnitrace-exe-write-log/instrumentation/user.log
|
||||
LABELS "log"
|
||||
TIMEOUT 60
|
||||
PASS_REGEX "user.log"
|
||||
FAIL_REGEX "No such file or directory|not found")
|
||||
|
||||
omnitrace_add_bin_test(
|
||||
NAME omnitrace-avail-help
|
||||
TARGET omnitrace-avail
|
||||
|
||||
Reference in new issue
Block a user