Commit 588861ac authored by Max Lyon's avatar Max Lyon

merge from ReForm

parents 86ada33b 5d268ba7
......@@ -41,10 +41,10 @@ private:
} //namespace Debug
#define DEB_time_session(SSSN, LL) \
#define DEB_time_session(SSSN, LL) PROGRESS_TICK; \
Debug::StopWatchSession __sw_sssn(deb, SSSN, LL);
#define DEB_time_session_def(SSSN) \
#define DEB_time_session_def(SSSN) PROGRESS_TICK; \
Debug::StopWatchSession __sw_sssn(deb, SSSN, 2);
#define DEB_time_func(LL) DEB_enter_func \
......@@ -54,13 +54,13 @@ private:
#else
#define DEB_time_session(SSSN, LL)
#define DEB_time_session(SSSN, LL) PROGRESS_TICK;
#define DEB_time_session_def(SSSN)
#define DEB_time_session_def(SSSN) PROGRESS_TICK;
#define DEB_time_func(LL)
#define DEB_time_func(LL) PROGRESS_TICK;
#define DEB_time_func_def
#define DEB_time_func_def PROGRESS_TICK;
#endif // DEB_ON
......
......@@ -5,18 +5,54 @@
#include "ProgressNode.hh"
#include "Base/Utils/BaseError.hh"
#include "Base/Utils/IOutputStream.hh"
#include "Base/Debug/DebOut.hh"
#include <exception>
#include <fstream>
#include <chrono>
#ifdef PROGRESS_ON
namespace Progress {
struct Context::LogData
{
typedef Base::OutputStreamAdaptT<std::ofstream> FileOutputStream;
FileOutputStream strm;
typedef std::chrono::high_resolution_clock Clock;
typedef Clock::time_point Time;
typedef std::chrono::seconds Seconds;
typedef std::chrono::milliseconds Milliseconds;
typedef std::chrono::nanoseconds Nanoseconds;
LogData() : strm(LOG_FILENAME) {}
void record_entry_time() { entr_time = Clock::now(); }
TickNumber elapsed_nanoseconds() const
{
const auto exit_time = Clock::now();
const auto elpd_time = exit_time - entr_time;
const auto elpd_mlsc = std::chrono::duration_cast<Nanoseconds>(elpd_time);
return elpd_mlsc.count();
}
private:
Time entr_time;
};
/// Context implementation
Context::Context()
: abrt_stte_(AST_NONE), abrt_alwd_(false), phny_root_("phony"),
root_node_(&phny_root_), actv_node_(&phny_root_)
root_node_(&phny_root_), actv_node_(&phny_root_), log_data_(nullptr)
{}
Context::~Context()
{
delete log_data_;
}
void Context::abort()
{
// Don't throw if abort is not permitted or
......@@ -44,15 +80,33 @@ void Context::end_abort()
abrt_stte_ = AST_NONE;
}
void Context::enter_node(Node* _node)
void Context::enter_node(Node* const _node)
{ // NOTE: this function is written carefully to allow asynchronous inspection
// of the node graph from a tracking thread. It assumes the tracking thread
// does not use the node parent.
_node->tick_nmbr_ = 0;
if (phony())
root_node_ = _node;
else
_node->prnt_ = actv_node_;
_node->done_chld_nmbr_ = 0;
if (phony()) // no active node, ...
root_node_ = _node; // ... so the node to enter is the root
else // the currently active node becomes the parent of the node to enter
{
_node->prnt_ = actv_node_; // make the parent the active node
int done_chld_nmbr = 0;
// seek the "active" child in the parent's list of children
auto chld = actv_node_->child();
for (; chld != nullptr && chld != _node; chld = chld->next())
++done_chld_nmbr;
DEB_error_if(chld != _node, "Could not find the child node " << _node->name
<< " in the children list of the parent node " << actv_node_->name);
actv_node_->done_chld_nmbr_ = done_chld_nmbr; // update the parent data
}
if (logging()) // record the entry time
{
if (actv_node_ != nullptr) // add the elapsed time to the active node
actv_node_->time_ns_ += log_data_->elapsed_nanoseconds();
_node->time_ns_ = 0; // restart counting in the active node
log_data_->record_entry_time(); // record the entry time into _node
}
actv_node_ = _node;
}
......@@ -60,11 +114,49 @@ void Context::exit_node()
{ // NOTE: this function is written carefully to allow asynchronous inspection
// of the node graph from a tracking thread. It assumes the tracking thread
// does not use the node parent.
if (phony())
return; // TODO: this condition is a bug, so we need a DEB_error() here?
if (phony())
{// this is a bug, but guard against it anyway since it's difficult to test
DEB_error("Context exits from the phony() node!");
return;
}
if (logging())
{// log the node on exit
actv_node_->time_ns_ += log_data_->elapsed_nanoseconds();
auto& os = log_data_->strm;
os << actv_node_->name << LOG_COMMA <<
actv_node_->tick_number() << LOG_COMMA <<
actv_node_->time_ns_ / 1000000 << LOG_COMMA; // convert to milliseconds
actv_node_->log(os);
os << Base::ENDL;
// record the re-entry time into the parent _node
log_data_->record_entry_time();
}
auto* const prnt = actv_node_->prnt_;
actv_node_->prnt_ = nullptr;
actv_node_ = prnt == nullptr ? &phny_root_ : prnt;
if (prnt == nullptr)
{
actv_node_ = &phny_root_;
return;
}
++prnt->done_chld_nmbr_; // this node is now done as well
actv_node_ = prnt; // activate the parent
}
void Context::set_logging(const bool _lgng)
{
if (_lgng == logging())
return;
if (_lgng)
log_data_ = new LogData; // turn on logging
else
{// turn off logging.
delete log_data_;
log_data_ = nullptr;
}
}
BASE_THREAD_LOCAL Context cntx;
......@@ -73,10 +165,18 @@ BASE_THREAD_LOCAL Context cntx;
Node::~Node()
{}
int Node::weight() const
{
return 1;
}
TickNumber Node::tick_number_max() const
{
return tick_nmbr_;
return 1;
}
void Node::log(IOutputStream& /*_os*/) const
{}
} //namespace Progress
#endif//PROGRESS_ON
......@@ -6,11 +6,31 @@
#ifdef PROGRESS_ON
#include <Base/Utils/Thread.hh>
#include <stddef.h>
namespace Base {
class IOutputStream;
}
namespace Progress {
//! Progress stream type
typedef Base::IOutputStream IOutputStream;
//! Progress tick counter type
typedef unsigned long long TickNumber;
typedef size_t TickNumber;
/*!
The name of the progress log file, uses the .csv format.
Each line contains comma-separated values for a progress node as follows:
node_name, tick#, ms#, param0, param1, ...
*/
const char* const LOG_FILENAME = "progress.csv";
//! The log value separator
const char* const LOG_COMMA = ", ";
//! Represent an operational node in the progress graph
class Node
......@@ -32,7 +52,8 @@ public:
Node(const char* const _name,
Node* _next = nullptr, Node* _chld = nullptr)
: name(_name), next_(_next), chld_(_chld), prnt_(nullptr), tick_nmbr_(0)
: name(_name), next_(_next), chld_(_chld), prnt_(nullptr),
tick_nmbr_(0), done_chld_nmbr_(0), time_ns_(0)
{}
virtual ~Node();
......@@ -40,16 +61,44 @@ public:
const Node* next() const { return next_; };
const Node* child() const { return chld_; };
void tick() { ++tick_nmbr_; }
//! Number of ticks recorded
TickNumber tick_number() const { return tick_nmbr_; }
//! Number of children that have been completed
int done_child_number() const { return done_chld_nmbr_; }
/*
Relative weight of this node in the overall computation process, default is 1.
*/
virtual int weight() const;
/*!
Estimate the maximum number of ticks in this node, default is 1.
The function is virtual so that the node can make use of some custom logic
and specific parameters.
*/
virtual TickNumber tick_number_max() const;
void tick() { ++tick_nmbr_; }
/*!
Log custom logic parameter values used by the estimation in tick_number_max().
The values have to be separated by \ref LOG_COMMA.
*/
virtual void log(IOutputStream& _os) const;
protected:
Node* next_; //!< next node on the same level, last if nullptr
Node* chld_; //!< first child node, leaf if nullptr
Node* prnt_; //!< parent node, set temporarily
TickNumber tick_nmbr_; // number of ticks in the node
TickNumber tick_nmbr_; /*!< number of ticks in the node, exclusive of the
ticks in child nodes */
int done_chld_nmbr_; //!< Number of child nodes that made their exit
private:
TickNumber time_ns_; /*!< Time in nanoseconds spent in the node since it was
entered, exclusive of the time spent in child nodes. This is recorded only
if the Context is logging, and is accessible only by the Context. */
friend class Context;
};
......@@ -59,6 +108,7 @@ class Context
public:
//! Default constructor
Context();
~Context();
/*!
Get if the progress is "faking" tracking, i.e., using a phony root node.
......@@ -75,6 +125,11 @@ public:
Get the root node, only makes sense to use that if not phony().
*/
const Node* root_node() const { return root_node_; }
/*!
Get if the context is active, i.e., there is an active operation in progress.
*/
bool active() const { return actv_node_ != root_node_; }
/*!
Request progress abort, intended to be called asynchronously from a "tracking"
......@@ -103,12 +158,18 @@ public:
abort();
}
void enter_node(Node* _node);
void enter_node(Node* const _node);
void exit_node();
void set_abort_allowed(bool _abrt_alwd) { abrt_alwd_ = _abrt_alwd; }
bool abort_allowed() const { return abrt_alwd_; }
//! Turn on/off progress logging
void set_logging(const bool _lgng);
//! Check if progress logging is on/off
bool logging() const { return log_data_ != nullptr; }
private:
enum AbortStateType
{
......@@ -117,12 +178,15 @@ private:
AST_PROCESSING
};
struct LogData;
private:
AbortStateType abrt_stte_; //!< Abort state
bool abrt_alwd_; //!< Abort allowed flag, ignores AST_REQUEST if false
Node phny_root_; //!< "phony" root
Node* root_node_; //!< Root node
Node* actv_node_; //!< Active node
LogData* log_data_; //!< Log data, exists only when logging
private:
//! Throws PROGRESS_ABORTED to abort the current operation.
......@@ -169,18 +233,18 @@ private:
} //namespace Progress {
#define PROGRESS_DEFINE_NODE_CUSTOM(TYPE, OPRT, ...) \
#define PROGRESS_DEFINE_NODE_CUSTOM(TYPE, OPRT, NAME, ...) \
BASE_THREAD_LOCAL Progress::Node* Progress::PROGRESS_NODE_NAME(OPRT) = nullptr; \
Progress::Node* Progress::PROGRESS_MAKE_NODE_NAME(OPRT) \
(Progress::Node* _next) \
{ \
static BASE_THREAD_LOCAL TYPE node(#OPRT, _next, \
static BASE_THREAD_LOCAL TYPE node(NAME, _next, \
Node::make_child_list( __VA_ARGS__ ));\
return OPRT##_node = &node; \
}
#define PROGRESS_DEFINE_NODE(OPRT, ...) \
PROGRESS_DEFINE_NODE_CUSTOM(Node, OPRT, ##__VA_ARGS__)
#define PROGRESS_DEFINE_NODE(OPRT, NAME, ...) \
PROGRESS_DEFINE_NODE_CUSTOM(Node, OPRT, NAME, ##__VA_ARGS__)
#if defined(_MSC_VER)
#define __PROGRESS_FUNCTION__ __FUNCTION__ // works in VC well
......
......@@ -6,6 +6,7 @@
#include "Base/Debug/DebCallStack.hh"
#include <fstream>
#include <iostream>
#include <mutex>
namespace Test {
namespace Checksum {
......@@ -137,20 +138,24 @@ Difference Object::compare(
void Object::add(const Result& _rslt, const String& _data)
{
static std::mutex mtx; //synchronize access to the checksum report stream
std::lock_guard<std::mutex> lock(mtx);
static Base::OutputStreamAdaptT<std::ofstream> test_str(REPORT_FILENAME);
static bool tag_out = false;
#ifdef DEB_ON
static String prev_call_stck;
String call_stck("/");
Debug::CallStack::query().append(call_stck);
if (prev_call_stck != call_stck)
{
test_str << call_stck << Base::LF;
prev_call_stck = call_stck;
}
#endif//DEB_ON
if (!tag_out)
{
tag_out = true;
......
......@@ -24,7 +24,9 @@
#define BASE_THREAD_LOCAL_BROKEN
#endif//__GNUC__
#ifdef BASE_THREAD_LOCAL_BROKEN // if broken do not use thread_local
// disable completely this and use static non-thread storage instead due to
// bugs in thread_local variable initialization in worker threads
#if 1 //def BASE_THREAD_LOCAL_BROKEN // if broken do not use thread_local
#define BASE_THREAD_LOCAL
#else// BASE_THREAD_LOCAL_BROKEN
#define BASE_THREAD_LOCAL thread_local
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment