Commit cf012644 authored by Martin Marinov's avatar Martin Marinov
Browse files

Refactored various debug class implementations from DebStream.cc. All unit...

Refactored various debug class implementations from DebStream.cc. All unit tests pass, but debug system bugs are possible.
parent 3a2ba575
set(my_headers
${CMAKE_CURRENT_SOURCE_DIR}/DebCallStack.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebConfig.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebDefault.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebError.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebFile.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebFileOut.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebOut.hh
${CMAKE_CURRENT_SOURCE_DIR}/DebTime.hh
......@@ -8,6 +12,9 @@ set(my_headers
)
set(my_sources
${CMAKE_CURRENT_SOURCE_DIR}/DebConfig.cc
${CMAKE_CURRENT_SOURCE_DIR}/DebCallStack.cc
${CMAKE_CURRENT_SOURCE_DIR}/DebFile.cc
${CMAKE_CURRENT_SOURCE_DIR}/DebFileOut.cc
${CMAKE_CURRENT_SOURCE_DIR}/DebStream.cc
PARENT_SCOPE
......
// (C) Copyright 2016 by Autodesk, Inc.
#ifdef DEB_ON
#include "DebCallStack.hh"
#include "DebOut.hh"
#include <cstring>
namespace Debug {
namespace {
// Replace interior of < > in function name with . for brevity
void compact_name(const char* _func, std::string& _str)
{
int cnt = 0;
const char* ptr = _func;
while (ptr && (*ptr != '\0'))
{
char c = *ptr;
if (c == '>') --cnt;
if (cnt == 0) _str.append(1, c);
if (c == '<')
{
if (cnt == 0) _str.append(".");
++cnt;
}
++ptr;
}
}
// Add to the string the call stack element string
void add_to_string(const Enter* _deb, std::string& _str,
const bool _strip_angled, const bool _with_counts,
const Enter* _prev)
{
if (_prev == nullptr || strcmp(_prev->function(), _deb->function()) != 0)
{
// Writes the function name if it is not the same as previous
// function in the call stack.
if (_prev != nullptr)
_str.append("->");
if (_strip_angled)
compact_name(_deb->function(), _str);
else
_str.append(_deb->function());
}
_str.append("[");
if (!_with_counts)
_str.append("*");
else
_str.append(std::to_string(_deb->number()));
_str.append("]");
}
}//namespace
CallStack& CallStack::modify()
{
static CallStack glbl_call_stck;
return glbl_call_stck;
}
const CallStack& CallStack::query()
{
return modify();
}
// Read a particular call stack element
//bool read(int _up, const char*& _funcname, int& _count)
//{
// const Enter* fcs = call(_up);
// if (fcs != nullptr)
// {
// _funcname = fcs->function();
// _count = fcs->number(); // Return most recent deb_enter_count
// return true;
// }
// return false;
//}
//
void CallStack::append(std::string& _str, const bool _with_counts) const
{
const Enter* prev = nullptr;
for (size_t i = 0, n = depth(); i < n; prev = calls_[i++])
add_to_string(calls_[i], _str, true, _with_counts, prev);
}
void CallStack::append_indent(std::string& _str, const int _indt,
const bool _html) const
{
if (_indt == 0)
return;
if (_html)
{
char buffer[64];
sprintf_s(buffer, sizeof(buffer), "<FONT SIZE=%i>", _indt);
_str.append(buffer);
}
int num = (int)calls_.size();
int i0 = 0;
if (!_html)
++i0; // Don't waste whitespace on first level indent if .txt
for (int i = i0; i < num; ++i)
_str.append(" ");
if (_html)
_str.append(":&nbsp;</FONT>\n");
}
}//namespace Debug
#endif//DEB_ON
// (C) Copyright 2016 by Autodesk, Inc.
#ifndef BASE_DEBCALLSTACK_HH_INCLUDED
#define BASE_DEBCALLSTACK_HH_INCLUDED
#ifdef DEB_ON
#include <string>
#include <vector>
namespace Debug {
class Enter;
typedef unsigned int uint;
//! A stack of Debug entry points, a partial record of the real call stack
class CallStack
{
public:
static const CallStack& query();
static CallStack& modify();
public:
bool empty() const { return calls_.empty(); }
size_t depth() const { return calls_.size(); }
void push(const Enter* const _entr) { calls_.push_back(_entr); }
void pop() { calls_.pop_back(); }
const Enter* call(const int _up = 0) const
{
size_t n = calls_.size();
if (_up < n)
return calls_[n - 1 - _up];
else
return nullptr;
}
/*!
Append the full call stack.
*/
void append(std::string& _str, const bool _with_counts = true) const;
/*!
Append and indent to the string
*/
void append_indent(std::string& _str, const int _indt, const bool _html
) const;
private:
std::vector<const Enter*> calls_;
private:
CallStack() {}
~CallStack() {}
CallStack(const CallStack&);
CallStack& operator=(const CallStack&);
};
};//namespace Debug
#endif//DEB_ON
#endif//BASE_DEBCALLSTACK_HH_INCLUDED
// (C) Copyright 2016 by Autodesk, Inc.
#ifdef DEB_ON
#include "DebConfig.hh"
#include "DebDefault.hh"
#include "Base/Utils/Environment.hh"
#include <fstream>
#include <sstream>
#include <list>
#include <string>
#include <map>
namespace Debug {
namespace {
// We use this data to decide the debug level of a function in a file.
class FilterLevelSelector
{
public:
void add_file_string(const std::string& _str)
{
file_selct_strngs_.push_back(_str);
}
void add_func_string(const std::string& _str)
{
func_selct_strngs_.push_back(_str);
}
bool select_file(const char* _flnm) const
{
// TODO: this should be possible to implement w/o a copy
std::string flnm(_flnm);
// TODO: this code below only works in ReForm, should be made to work
// for IGM, CoMISo, etc
const std::string root_dir("ReForm");
auto pos = flnm.rfind(root_dir);
if (pos != std::string::npos)
flnm = flnm.substr(pos + root_dir.size());
return search(flnm, file_selct_strngs_);
}
bool select_function(const char* _func) const
{
return search(_func, func_selct_strngs_);
}
private:
static bool search(const std::string& _flnm,
const std::list<std::string>& _sel_strings)
{
for (const auto& sel : _sel_strings)
{
if (_flnm.find(sel) != std::string::npos)
return true;
}
return false;
}
private:
// list of strings to be found inside the file name.
std::list<std::string> file_selct_strngs_;
// list of strings to be found inside the function name.
std::list<std::string> func_selct_strngs_;
};
}//namespace
class Config::Impl
{
public:
Impl() : dflt_lvl_(Default::LEVEL) { read(); }
void read();
int level(const char* const _flnm, const char* const _fnct) const;
private:
int dflt_lvl_;
typedef std::map<int, FilterLevelSelector> LevelFilterMap;
LevelFilterMap lvl_fltrs_; // filters for each level
};
void Config::Impl::read()
{
const auto flnm =
System::Environment::variable("REFORM_DEB_CONFIG", "reform_deb.cfg");
std::ifstream cnfg_strm(flnm.c_str());
std::string line;
while (std::getline(cnfg_strm, line))
{
std::stringstream line_stream(line);
std::string type;
line_stream >> type;
void (FilterLevelSelector::*add_string)(const std::string&) = nullptr;
if (type == "all")
{}
else if (type == "file")
add_string = &FilterLevelSelector::add_file_string;
else if (type == "func")
add_string = &FilterLevelSelector::add_func_string;
else
continue;
int lvl;
line_stream >> lvl;
if (add_string == nullptr)
{
dflt_lvl_ = lvl; // We have read the default level.
continue;
}
char colon;
line_stream >> colon;
if (colon != ':')
continue;
std::string select_str;
while(line_stream >> select_str)
(lvl_fltrs_[lvl].*add_string)(select_str);
}
}
int Config::Impl::level(const char* const _flnm, const char* const _fnct) const
{
int lvl = dflt_lvl_;
for (LevelFilterMap::const_iterator fltr_it = lvl_fltrs_.begin();
fltr_it != lvl_fltrs_.end(); ++fltr_it)
{// continue this iteration until the maximum allowed level if found
const LevelFilterMap::value_type& fltr = *fltr_it;
if (lvl >= fltr.first) // can this filter increase the current level?
continue;
if (fltr.second.select_file(_flnm) || fltr.second.select_function(_fnct))
lvl = fltr.first;
}
return lvl;
}
//////////////////////////////////////////////////////////////////////////
const Config& Config::query()
{
static Config glbl_cnfg;
return glbl_cnfg;
}
Config::Config() : impl_(new Impl) {}
Config::~Config() { delete impl_; }
int Config::level(const char* const _flnm, const char* const _fnct) const
{
return impl_->level(_flnm, _fnct);
}
}//namespace Debug
#endif//DEB_ON
// (C) Copyright 2016 by Autodesk, Inc.
#ifndef BASE_DEBCONFIG_HH_INCLUDED
#define BASE_DEBCONFIG_HH_INCLUDED
#ifdef DEB_ON
#include <string>
#include <vector>
namespace Debug {
/*!
Access the global, per-process, configuration options of the Debug system.
*/
class Config
{
public:
static const Config& query();
public:
//! Get the level for this filename and function
int level(const char* const _flnm, const char* const _fnct) const;
private:
class Impl;
Impl* impl_;
private:
//! Private constructor
Config();
//! Private destructor
~Config();
//! Disable copy
Config(const Config&);
//! Disable assignment
Config& operator=(const Config&);
};
};//namespace Debug
#endif//DEB_ON
#endif//BASE_DEBCONFIG_HH_INCLUDED
// (C) Copyright 2016 by Autodesk, Inc.
//
// The information contained herein is confidential, proprietary
// to Autodesk, Inc., and considered a trade secret as defined
// in section 499C of the penal code of the State of California.
// Use of this information by anyone other than authorized
// employees of Autodesk, Inc. is granted only under a written
// non-disclosure agreement, expressly prescribing the scope
// and manner of such use.
#ifndef BASE_DEBDEFAULT_HH_INCLUDED
#define BASE_DEBDEFAULT_HH_INCLUDED
......@@ -15,6 +7,7 @@ namespace Debug {
namespace Default {
const char* const LOG_FILENAME = "reform_deb_out.txt";
const int LEVEL = 5;
}//Default
}//Debug
......
// (C) Copyright 2016 by Autodesk, Inc.
#ifdef DEB_ON
#include "DebFile.hh"
#include "DebCallStack.hh"
#include "DebDefault.hh"
#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
#include <map>
#include <memory>
#include <list>
#include <map>
#include <sstream>
#include <cstring>
#include <time.h>
#ifndef WIN32
#define sprintf_s snprintf
#endif
namespace Debug {
namespace {
// TODO: make this use std::string; check for html extension; case insensitive
bool is_html_filename(const char* const str)
{
if (str == nullptr) return false;
const char* dot = strrchr(str, '.');
if (dot == nullptr) return false;
++dot;
return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
}
}//namespace
class File::Impl
{
public:
enum Flags
{
APPEND = 0x01,
HTML = 0x02,
RETAIN = 0x04,
KEEP_OPEN = 0x08
};
public:
Impl(const char* const _flnm, const uint _flags = APPEND | RETAIN)
: flags_(_flags), num_flush_(0)
{
set_filename(_flnm);
indent_size_ = 3;
at_line_start_ = false; // Don't want to indent header
}
~Impl()
{
close();
clear();
}
bool is_kept_open() const
{
return 0 != (flags_ & KEEP_OPEN);
}
bool is_html() const
{
return 0 != (flags_ & HTML);
}
bool is_retained() const
{
return 0 != (flags_ & RETAIN);
}
bool is_appended() const
{
return 0 != (flags_ & APPEND);
}
// Only applies to HTML DEB_out
bool is_white_on_black() const
{
return true;
}
int indent_size()
{
return indent_size_;
}
bool file_is_open() const
{
return file_stream_.is_open();
}
int priority() const
{
return priority_;
}
bool fork_to_cout()
{
return false;
}
bool fork_to_cerr()
{
return true;
}
const char* filename() const
{
if (this && (!flnm_.empty())) return flnm_.c_str();
return nullptr;
}
void clear()
{
current_.clear();
output_.clear();
flnm_.clear();
}
char prev_char() const
{
if (!current_.empty())
return current_.back();
if (!output_.empty())
return output_.back();
return '\0';
}
void indent(bool _full_text)
{
if (indent_size() == 0)
return;
CallStack::query().append_indent(current_, indent_size(),
_full_text && is_html());
}
void line_break(bool _with_indent = false)
{
if (is_html())
current_.append("<br>"); // Don't bother with matching </br>
current_.append("\n", 1);
if (_with_indent)
indent(false);
else
at_line_start_ = true;
}
void print_direct(const std::string& _s)
{
current_.append(_s);
}
void print(const char _c)
{
if (_c == '\n')
{
line_break();
return;
}
if (at_line_start_)
{
indent(true);
at_line_start_ = false;
}
if (is_html())
{
// translate the esoteric characters used in IGM DEB_out
if (_c == -62 ) // -62
return;
if (_c == -89) // 167 = -89
{
current_.append("&sect;");
return;
}
if (_c == -80) // -80
{
current_.append("&deg;");
return;
}
}
current_.append(&_c, 1);
}
void print_to_ostream(const char* const _s, std::ostream& os)
{
os << _s;
}
void print(const char* const _s, bool _fork = true)
<