aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/lwtrace/mon/mon_lwtrace.cpp
diff options
context:
space:
mode:
authorserxa <serxa@yandex-team.ru>2022-02-10 16:49:08 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:49:08 +0300
commite5d4696304c6689379ac7ce334512404d4b7836c (patch)
tree5d5cb817648f650d76cf1076100726fd9b8448e8 /library/cpp/lwtrace/mon/mon_lwtrace.cpp
parentd6d7db348c2cc64e71243cab9940ee6778f4317d (diff)
downloadydb-e5d4696304c6689379ac7ce334512404d4b7836c.tar.gz
Restoring authorship annotation for <serxa@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'library/cpp/lwtrace/mon/mon_lwtrace.cpp')
-rw-r--r--library/cpp/lwtrace/mon/mon_lwtrace.cpp7878
1 files changed, 3939 insertions, 3939 deletions
diff --git a/library/cpp/lwtrace/mon/mon_lwtrace.cpp b/library/cpp/lwtrace/mon/mon_lwtrace.cpp
index b400103f58..a61ee9ce22 100644
--- a/library/cpp/lwtrace/mon/mon_lwtrace.cpp
+++ b/library/cpp/lwtrace/mon/mon_lwtrace.cpp
@@ -1,8 +1,8 @@
#include "mon_lwtrace.h"
-#include <algorithm>
-#include <iterator>
-
+#include <algorithm>
+#include <iterator>
+
#include <google/protobuf/text_format.h>
#include <library/cpp/lwtrace/mon/analytics/all.h>
#include <library/cpp/lwtrace/all.h>
@@ -12,14 +12,14 @@
#include <library/cpp/resource/resource.h>
#include <library/cpp/string_utils/base64/base64.h>
#include <library/cpp/html/pcdata/pcdata.h>
-#include <util/string/escape.h>
-#include <util/system/condvar.h>
-#include <util/system/execpath.h>
+#include <util/string/escape.h>
+#include <util/system/condvar.h>
+#include <util/system/execpath.h>
#include <util/system/hostname.h>
using namespace NMonitoring;
-#define WWW_CHECK(cond, ...) \
+#define WWW_CHECK(cond, ...) \
do { \
if (!(cond)) { \
ythrow yexception() << Sprintf(__VA_ARGS__); \
@@ -27,1383 +27,1383 @@ using namespace NMonitoring;
} while (false) \
/**/
-#define WWW_HTML_INNER(out) HTML(out) WITH_SCOPED(tmp, TScopedHtmlInner(out))
-#define WWW_HTML(out) out << NMonitoring::HTTPOKHTML; WWW_HTML_INNER(out)
-
-namespace NLwTraceMonPage {
-
-struct TTrackLogRefs {
- struct TItem {
- const TThread::TId ThreadId;
- const NLWTrace::TLogItem* Ptr;
-
- TItem()
- : ThreadId(0)
- , Ptr(nullptr)
- {}
-
- TItem(TThread::TId tid, const NLWTrace::TLogItem& ref)
- : ThreadId(tid)
- , Ptr(&ref)
- {}
-
- TItem(const TItem& o)
- : ThreadId(o.ThreadId)
- , Ptr(o.Ptr)
- {}
-
- operator const NLWTrace::TLogItem&() const { return *Ptr; }
- };
-
+#define WWW_HTML_INNER(out) HTML(out) WITH_SCOPED(tmp, TScopedHtmlInner(out))
+#define WWW_HTML(out) out << NMonitoring::HTTPOKHTML; WWW_HTML_INNER(out)
+
+namespace NLwTraceMonPage {
+
+struct TTrackLogRefs {
+ struct TItem {
+ const TThread::TId ThreadId;
+ const NLWTrace::TLogItem* Ptr;
+
+ TItem()
+ : ThreadId(0)
+ , Ptr(nullptr)
+ {}
+
+ TItem(TThread::TId tid, const NLWTrace::TLogItem& ref)
+ : ThreadId(tid)
+ , Ptr(&ref)
+ {}
+
+ TItem(const TItem& o)
+ : ThreadId(o.ThreadId)
+ , Ptr(o.Ptr)
+ {}
+
+ operator const NLWTrace::TLogItem&() const { return *Ptr; }
+ };
+
using TItems = TVector<TItem>;
- TItems Items;
-
- TTrackLogRefs() {}
-
- TTrackLogRefs(const TTrackLogRefs& other)
- : Items(other.Items)
- {}
-
- void Clear()
- {
- Items.clear();
- }
-
- ui64 GetTimestampCycles() const
- {
- return Items.empty()? 0: Items.front().Ptr->GetTimestampCycles();
- }
-};
-
-//
-// Templates to treat both NLWTrace::TLogItem and NLWTrace::TTrackLog in the same way (e.g. in TLogQuery)
-//
-
-template <class TLog>
-struct TLogTraits {};
-
-template <>
-struct TLogTraits<NLWTrace::TLogItem> {
- using TLog = NLWTrace::TLogItem;
- using const_iterator = const NLWTrace::TLogItem*;
- using const_reverse_iterator = const NLWTrace::TLogItem*;
-
- static const_iterator begin(const TLog& log) { return &log; }
- static const_iterator end(const TLog& log) { return &log + 1; }
- static const_reverse_iterator rbegin(const TLog& log) { return &log; }
- static const_reverse_iterator rend(const TLog& log) { return &log + 1; }
- static bool empty(const TLog&) { return false; }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log; }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log; }
-};
-
-template <>
-struct TLogTraits<NLWTrace::TTrackLog> {
- using TLog = NLWTrace::TTrackLog;
- using const_iterator = NLWTrace::TTrackLog::TItems::const_iterator;
- using const_reverse_iterator = NLWTrace::TTrackLog::TItems::const_reverse_iterator;
-
- static const_iterator begin(const TLog& log) { return log.Items.begin(); }
- static const_iterator end(const TLog& log) { return log.Items.end(); }
- static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
- static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
- static bool empty(const TLog& log) { return log.Items.empty(); }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
-};
-
-template <>
-struct TLogTraits<TTrackLogRefs> {
- using TLog = TTrackLogRefs;
- using const_iterator = TTrackLogRefs::TItems::const_iterator;
- using const_reverse_iterator = TTrackLogRefs::TItems::const_reverse_iterator;
-
- static const_iterator begin(const TLog& log) { return log.Items.begin(); }
- static const_iterator end(const TLog& log) { return log.Items.end(); }
- static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
- static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
- static bool empty(const TLog& log) { return log.Items.empty(); }
- static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
- static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
-};
-
-/*
- * Log Query Language:
- * Data expressions:
- * 1) myparam[0], myparam[-1] // the first and the last myparam in any probe/provider
- * 2) myparam // the first (the same as [0])
- * 3) PROVIDER..myparam // any probe with myparam in PROVIDER
- * 4) MyProbe._elapsedMs // Ms since track begin for the first MyProbe event
- * 5) PROVIDER.MyProbe._sliceUs // Us since previous event in track for the first PROVIDER.MyProbe event
- */
-
-struct TLogQuery {
-private:
- enum ESpecialParam {
- NotSpecial = 0,
- // The last '*' can be one of: Ms, Us, Ns, Min, Hours, (blank means seconds)
- // '*Time' can be one of: RTime (since cut ts for given dataset), NTime (negative time since now), Time (since machine start)
- TrackDuration = 1, // _track*
- TrackBeginTime = 2, // _begin*Time*
- TrackEndTime = 3, // _end*Time*
- ElapsedDuration = 4, // _elapsed*
- SliceDuration = 5, // _slice*
- ThreadTime = 6, // _thr*Time*
- };
-
- template <class TLog, class TTr = TLogTraits<TLog>>
- struct TExecuteQuery;
-
- template <class TLog, class TTr>
- friend struct TExecuteQuery;
-
- template <class TLog, class TTr>
- struct TExecuteQuery {
- const TLogQuery& Query;
- const TLog* Log = nullptr;
- bool Reversed;
-
- i64 Skip;
- const NLWTrace::TLogItem* Item = nullptr;
- typename TTr::const_iterator FwdIter;
- typename TTr::const_reverse_iterator RevIter;
-
- NLWTrace::TTypedParam Result;
-
- explicit TExecuteQuery(const TLogQuery& query, const TLog& log)
- : Query(query)
- , Log(&log)
- , Reversed(Query.Index < 0)
- , Skip(Reversed? -Query.Index - 1: Query.Index)
- , FwdIter()
- , RevIter()
- {}
-
- void ExecuteQuery()
- {
- if (!Reversed) {
- for (auto i = TTr::begin(*Log), e = TTr::end(*Log); i != e; ++i) {
- if (FwdIteration(i)) {
- return;
- }
- }
- } else {
- for (auto i = TTr::rbegin(*Log), e = TTr::rend(*Log); i != e; ++i) {
- if (RevIteration(i)) {
- return;
- }
- }
- }
- }
-
- bool FwdIteration(typename TTr::const_iterator it)
- {
- FwdIter = it;
- Item = &*it;
- return ProcessItem();
- }
-
- bool RevIteration(typename TTr::const_reverse_iterator it)
- {
- RevIter = it;
- Item = &*it;
- return ProcessItem();
- }
-
- bool ProcessItem()
- {
- if (Query.Provider && Query.Provider != Item->Probe->Event.GetProvider()) {
- return false;
- }
- if (Query.Probe && Query.Probe != Item->Probe->Event.Name) {
- return false;
- }
- switch (Query.SpecialParam) {
- case NotSpecial:
- if (Item->Probe->Event.Signature.FindParamIndex(Query.ParamName) != size_t(-1)) {
- break; // param found
- } else {
- return false; // param not found
- }
- case TrackDuration: Y_FAIL();
- case TrackBeginTime: Y_FAIL();
- case TrackEndTime: Y_FAIL();
- case ElapsedDuration: break;
- case SliceDuration: break;
- case ThreadTime: break;
- }
- if (Skip > 0) {
- Skip--;
- return false;
- }
- switch (Query.SpecialParam) {
- case NotSpecial:
- Result = NLWTrace::TTypedParam(Item->GetParam(Query.ParamName));
- return true;
- case TrackDuration: Y_FAIL();
- case TrackBeginTime: Y_FAIL();
- case TrackEndTime: Y_FAIL();
- case ElapsedDuration:
- Result = NLWTrace::TTypedParam(Query.Duration(
- Log->GetTimestampCycles(),
- Item->GetTimestampCycles()));
- return true;
- case SliceDuration:
- Result = NLWTrace::TTypedParam(Query.Duration(
- PrevOrSame().GetTimestampCycles(),
- Item->GetTimestampCycles()));
- return true;
- case ThreadTime:
- Result = NLWTrace::TTypedParam(Query.Instant(Item->GetTimestampCycles()));
- return true;
- }
- return true;
- }
-
- const NLWTrace::TLogItem& PrevOrSame() const
- {
- if (!Reversed) {
- auto i = FwdIter;
- if (i != TTr::begin(*Log)) {
- i--;
- }
- return *i;
- } else {
- auto j = RevIter + 1;
- if (j == TTr::rend(*Log)) {
- return *RevIter;
- }
- return *j;
- }
- }
- };
-
+ TItems Items;
+
+ TTrackLogRefs() {}
+
+ TTrackLogRefs(const TTrackLogRefs& other)
+ : Items(other.Items)
+ {}
+
+ void Clear()
+ {
+ Items.clear();
+ }
+
+ ui64 GetTimestampCycles() const
+ {
+ return Items.empty()? 0: Items.front().Ptr->GetTimestampCycles();
+ }
+};
+
+//
+// Templates to treat both NLWTrace::TLogItem and NLWTrace::TTrackLog in the same way (e.g. in TLogQuery)
+//
+
+template <class TLog>
+struct TLogTraits {};
+
+template <>
+struct TLogTraits<NLWTrace::TLogItem> {
+ using TLog = NLWTrace::TLogItem;
+ using const_iterator = const NLWTrace::TLogItem*;
+ using const_reverse_iterator = const NLWTrace::TLogItem*;
+
+ static const_iterator begin(const TLog& log) { return &log; }
+ static const_iterator end(const TLog& log) { return &log + 1; }
+ static const_reverse_iterator rbegin(const TLog& log) { return &log; }
+ static const_reverse_iterator rend(const TLog& log) { return &log + 1; }
+ static bool empty(const TLog&) { return false; }
+ static const NLWTrace::TLogItem& front(const TLog& log) { return log; }
+ static const NLWTrace::TLogItem& back(const TLog& log) { return log; }
+};
+
+template <>
+struct TLogTraits<NLWTrace::TTrackLog> {
+ using TLog = NLWTrace::TTrackLog;
+ using const_iterator = NLWTrace::TTrackLog::TItems::const_iterator;
+ using const_reverse_iterator = NLWTrace::TTrackLog::TItems::const_reverse_iterator;
+
+ static const_iterator begin(const TLog& log) { return log.Items.begin(); }
+ static const_iterator end(const TLog& log) { return log.Items.end(); }
+ static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
+ static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
+ static bool empty(const TLog& log) { return log.Items.empty(); }
+ static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
+ static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
+};
+
+template <>
+struct TLogTraits<TTrackLogRefs> {
+ using TLog = TTrackLogRefs;
+ using const_iterator = TTrackLogRefs::TItems::const_iterator;
+ using const_reverse_iterator = TTrackLogRefs::TItems::const_reverse_iterator;
+
+ static const_iterator begin(const TLog& log) { return log.Items.begin(); }
+ static const_iterator end(const TLog& log) { return log.Items.end(); }
+ static const_reverse_iterator rbegin(const TLog& log) { return log.Items.rbegin(); }
+ static const_reverse_iterator rend(const TLog& log) { return log.Items.rend(); }
+ static bool empty(const TLog& log) { return log.Items.empty(); }
+ static const NLWTrace::TLogItem& front(const TLog& log) { return log.Items.front(); }
+ static const NLWTrace::TLogItem& back(const TLog& log) { return log.Items.back(); }
+};
+
+/*
+ * Log Query Language:
+ * Data expressions:
+ * 1) myparam[0], myparam[-1] // the first and the last myparam in any probe/provider
+ * 2) myparam // the first (the same as [0])
+ * 3) PROVIDER..myparam // any probe with myparam in PROVIDER
+ * 4) MyProbe._elapsedMs // Ms since track begin for the first MyProbe event
+ * 5) PROVIDER.MyProbe._sliceUs // Us since previous event in track for the first PROVIDER.MyProbe event
+ */
+
+struct TLogQuery {
+private:
+ enum ESpecialParam {
+ NotSpecial = 0,
+ // The last '*' can be one of: Ms, Us, Ns, Min, Hours, (blank means seconds)
+ // '*Time' can be one of: RTime (since cut ts for given dataset), NTime (negative time since now), Time (since machine start)
+ TrackDuration = 1, // _track*
+ TrackBeginTime = 2, // _begin*Time*
+ TrackEndTime = 3, // _end*Time*
+ ElapsedDuration = 4, // _elapsed*
+ SliceDuration = 5, // _slice*
+ ThreadTime = 6, // _thr*Time*
+ };
+
+ template <class TLog, class TTr = TLogTraits<TLog>>
+ struct TExecuteQuery;
+
+ template <class TLog, class TTr>
+ friend struct TExecuteQuery;
+
+ template <class TLog, class TTr>
+ struct TExecuteQuery {
+ const TLogQuery& Query;
+ const TLog* Log = nullptr;
+ bool Reversed;
+
+ i64 Skip;
+ const NLWTrace::TLogItem* Item = nullptr;
+ typename TTr::const_iterator FwdIter;
+ typename TTr::const_reverse_iterator RevIter;
+
+ NLWTrace::TTypedParam Result;
+
+ explicit TExecuteQuery(const TLogQuery& query, const TLog& log)
+ : Query(query)
+ , Log(&log)
+ , Reversed(Query.Index < 0)
+ , Skip(Reversed? -Query.Index - 1: Query.Index)
+ , FwdIter()
+ , RevIter()
+ {}
+
+ void ExecuteQuery()
+ {
+ if (!Reversed) {
+ for (auto i = TTr::begin(*Log), e = TTr::end(*Log); i != e; ++i) {
+ if (FwdIteration(i)) {
+ return;
+ }
+ }
+ } else {
+ for (auto i = TTr::rbegin(*Log), e = TTr::rend(*Log); i != e; ++i) {
+ if (RevIteration(i)) {
+ return;
+ }
+ }
+ }
+ }
+
+ bool FwdIteration(typename TTr::const_iterator it)
+ {
+ FwdIter = it;
+ Item = &*it;
+ return ProcessItem();
+ }
+
+ bool RevIteration(typename TTr::const_reverse_iterator it)
+ {
+ RevIter = it;
+ Item = &*it;
+ return ProcessItem();
+ }
+
+ bool ProcessItem()
+ {
+ if (Query.Provider && Query.Provider != Item->Probe->Event.GetProvider()) {
+ return false;
+ }
+ if (Query.Probe && Query.Probe != Item->Probe->Event.Name) {
+ return false;
+ }
+ switch (Query.SpecialParam) {
+ case NotSpecial:
+ if (Item->Probe->Event.Signature.FindParamIndex(Query.ParamName) != size_t(-1)) {
+ break; // param found
+ } else {
+ return false; // param not found
+ }
+ case TrackDuration: Y_FAIL();
+ case TrackBeginTime: Y_FAIL();
+ case TrackEndTime: Y_FAIL();
+ case ElapsedDuration: break;
+ case SliceDuration: break;
+ case ThreadTime: break;
+ }
+ if (Skip > 0) {
+ Skip--;
+ return false;
+ }
+ switch (Query.SpecialParam) {
+ case NotSpecial:
+ Result = NLWTrace::TTypedParam(Item->GetParam(Query.ParamName));
+ return true;
+ case TrackDuration: Y_FAIL();
+ case TrackBeginTime: Y_FAIL();
+ case TrackEndTime: Y_FAIL();
+ case ElapsedDuration:
+ Result = NLWTrace::TTypedParam(Query.Duration(
+ Log->GetTimestampCycles(),
+ Item->GetTimestampCycles()));
+ return true;
+ case SliceDuration:
+ Result = NLWTrace::TTypedParam(Query.Duration(
+ PrevOrSame().GetTimestampCycles(),
+ Item->GetTimestampCycles()));
+ return true;
+ case ThreadTime:
+ Result = NLWTrace::TTypedParam(Query.Instant(Item->GetTimestampCycles()));
+ return true;
+ }
+ return true;
+ }
+
+ const NLWTrace::TLogItem& PrevOrSame() const
+ {
+ if (!Reversed) {
+ auto i = FwdIter;
+ if (i != TTr::begin(*Log)) {
+ i--;
+ }
+ return *i;
+ } else {
+ auto j = RevIter + 1;
+ if (j == TTr::rend(*Log)) {
+ return *RevIter;
+ }
+ return *j;
+ }
+ }
+ };
+
TString Text;
TString Provider;
TString Probe;
TString ParamName;
- ESpecialParam SpecialParam = NotSpecial;
- i64 Index = 0;
- double TimeUnitSec = 1.0;
- i64 ZeroTs = 0;
- i64 RTimeZeroTs = 0;
- i64 NTimeZeroTs = 0;
-
-public:
- TLogQuery() {}
-
+ ESpecialParam SpecialParam = NotSpecial;
+ i64 Index = 0;
+ double TimeUnitSec = 1.0;
+ i64 ZeroTs = 0;
+ i64 RTimeZeroTs = 0;
+ i64 NTimeZeroTs = 0;
+
+public:
+ TLogQuery() {}
+
explicit TLogQuery(const TString& text)
- : Text(text)
- {
- try {
- if (!Text.empty()) {
- ParseQuery(Text);
- }
- } catch (...) {
- ythrow yexception()
- << CurrentExceptionMessage()
- << " while parsing track log query: "
- << Text;
- }
- }
-
- operator bool() const
- {
- return !Text.empty();
- }
-
- template <class TLog>
- NLWTrace::TTypedParam ExecuteQuery(const TLog& log) const
- {
- using TTr = TLogTraits<TLog>;
-
- WWW_CHECK(Text, "execute of empty log query");
- if (TTr::empty(log)) {
- return NLWTrace::TTypedParam();
- }
-
- if (SpecialParam == TrackDuration) {
- return Duration(
- log.GetTimestampCycles(),
- TTr::back(log).GetTimestampCycles());
- } else if (SpecialParam == TrackBeginTime) {
- return Instant(log.GetTimestampCycles());
- } else if (SpecialParam == TrackEndTime) {
- return Instant(TTr::back(log).GetTimestampCycles());
- }
-
- TExecuteQuery<TLog, TTr> exec(*this, log);
- exec.ExecuteQuery();
- return exec.Result;
- }
-
-private:
- NLWTrace::TTypedParam Duration(ui64 ts1, ui64 ts2) const
- {
- double sec = NHPTimer::GetSeconds(ts1 < ts2? ts2 - ts1: 0);
- return NLWTrace::TTypedParam(sec / TimeUnitSec);
- }
-
- NLWTrace::TTypedParam Instant(ui64 ts) const
- {
- double sec = NHPTimer::GetSeconds(i64(ts) - ZeroTs);
- return NLWTrace::TTypedParam(sec / TimeUnitSec);
- }
-
+ : Text(text)
+ {
+ try {
+ if (!Text.empty()) {
+ ParseQuery(Text);
+ }
+ } catch (...) {
+ ythrow yexception()
+ << CurrentExceptionMessage()
+ << " while parsing track log query: "
+ << Text;
+ }
+ }
+
+ operator bool() const
+ {
+ return !Text.empty();
+ }
+
+ template <class TLog>
+ NLWTrace::TTypedParam ExecuteQuery(const TLog& log) const
+ {
+ using TTr = TLogTraits<TLog>;
+
+ WWW_CHECK(Text, "execute of empty log query");
+ if (TTr::empty(log)) {
+ return NLWTrace::TTypedParam();
+ }
+
+ if (SpecialParam == TrackDuration) {
+ return Duration(
+ log.GetTimestampCycles(),
+ TTr::back(log).GetTimestampCycles());
+ } else if (SpecialParam == TrackBeginTime) {
+ return Instant(log.GetTimestampCycles());
+ } else if (SpecialParam == TrackEndTime) {
+ return Instant(TTr::back(log).GetTimestampCycles());
+ }
+
+ TExecuteQuery<TLog, TTr> exec(*this, log);
+ exec.ExecuteQuery();
+ return exec.Result;
+ }
+
+private:
+ NLWTrace::TTypedParam Duration(ui64 ts1, ui64 ts2) const
+ {
+ double sec = NHPTimer::GetSeconds(ts1 < ts2? ts2 - ts1: 0);
+ return NLWTrace::TTypedParam(sec / TimeUnitSec);
+ }
+
+ NLWTrace::TTypedParam Instant(ui64 ts) const
+ {
+ double sec = NHPTimer::GetSeconds(i64(ts) - ZeroTs);
+ return NLWTrace::TTypedParam(sec / TimeUnitSec);
+ }
+
void ParseQuery(const TString& s)
- {
+ {
auto parts = SplitString(s, ".");
- WWW_CHECK(parts.size() <= 3, "too many name specifiers");
+ WWW_CHECK(parts.size() <= 3, "too many name specifiers");
ParseParamSelector(parts.back());
- if (parts.size() >= 2) {
- ParseProbeSelector(parts[parts.size() - 2]);
- }
- if (parts.size() >= 3) {
- ParseProviderSelector(parts[parts.size() - 3]);
- }
- }
-
+ if (parts.size() >= 2) {
+ ParseProbeSelector(parts[parts.size() - 2]);
+ }
+ if (parts.size() >= 3) {
+ ParseProviderSelector(parts[parts.size() - 3]);
+ }
+ }
+
void ParseParamSelector(const TString& s)
- {
- size_t bracket = s.find('[');
+ {
+ size_t bracket = s.find('[');
if (bracket == TString::npos) {
- ParseParamName(s);
- Index = 0;
- } else {
- ParseParamName(s.substr(0, bracket));
- size_t bracket2 = s.find(']', bracket);
+ ParseParamName(s);
+ Index = 0;
+ } else {
+ ParseParamName(s.substr(0, bracket));
+ size_t bracket2 = s.find(']', bracket);
WWW_CHECK(bracket2 != TString::npos, "closing braket ']' is missing");
- Index = FromString<i64>(s.substr(bracket + 1, bracket2 - bracket - 1));
- }
- }
-
+ Index = FromString<i64>(s.substr(bracket + 1, bracket2 - bracket - 1));
+ }
+ }
+
void ParseParamName(const TString& s)
- {
- ParamName = s;
+ {
+ ParamName = s;
TString paramName = s;
-
+
const static TVector<std::pair<TString, ESpecialParam>> specials = {
- { "_track", TrackDuration },
- { "_begin", TrackBeginTime },
- { "_end", TrackEndTime },
- { "_elapsed", ElapsedDuration },
- { "_slice", SliceDuration },
- { "_thr", ThreadTime }
- };
-
- // Check for special params
- SpecialParam = NotSpecial;
- for (const auto& p : specials) {
- if (paramName.StartsWith(p.first)) {
- SpecialParam = p.second;
+ { "_track", TrackDuration },
+ { "_begin", TrackBeginTime },
+ { "_end", TrackEndTime },
+ { "_elapsed", ElapsedDuration },
+ { "_slice", SliceDuration },
+ { "_thr", ThreadTime }
+ };
+
+ // Check for special params
+ SpecialParam = NotSpecial;
+ for (const auto& p : specials) {
+ if (paramName.StartsWith(p.first)) {
+ SpecialParam = p.second;
paramName.erase(0, p.first.size());
- break;
- }
- }
-
- if (SpecialParam == NotSpecial) {
- return;
- }
-
+ break;
+ }
+ }
+
+ if (SpecialParam == NotSpecial) {
+ return;
+ }
+
const static TVector<std::pair<TString, double>> timeUnits = {
- { "Ms", 1e-3 },
- { "Us", 1e-6 },
- { "Ns", 1e-9 },
- { "Min", 60.0 },
- { "Hours", 3600.0 }
- };
-
- // Parse units for special params
- TimeUnitSec = 1.0;
- for (const auto& p : timeUnits) {
- if (paramName.EndsWith(p.first)) {
- TimeUnitSec = p.second;
+ { "Ms", 1e-3 },
+ { "Us", 1e-6 },
+ { "Ns", 1e-9 },
+ { "Min", 60.0 },
+ { "Hours", 3600.0 }
+ };
+
+ // Parse units for special params
+ TimeUnitSec = 1.0;
+ for (const auto& p : timeUnits) {
+ if (paramName.EndsWith(p.first)) {
+ TimeUnitSec = p.second;
paramName.erase(paramName.size() - p.first.size());
- break;
- }
- }
-
- if (SpecialParam == ThreadTime ||
- SpecialParam == TrackBeginTime ||
- SpecialParam == TrackEndTime)
- {
- // Parse time zero for special instant params
+ break;
+ }
+ }
+
+ if (SpecialParam == ThreadTime ||
+ SpecialParam == TrackBeginTime ||
+ SpecialParam == TrackEndTime)
+ {
+ // Parse time zero for special instant params
const TVector<std::pair<TString, i64>> timeZeros = {
- { "RTime", RTimeZeroTs },
- { "NTime", NTimeZeroTs },
- { "Time", 0ll }
- };
- ZeroTs = -1;
- for (const auto& p : timeZeros) {
- if (paramName.EndsWith(p.first)) {
- ZeroTs = p.second;
+ { "RTime", RTimeZeroTs },
+ { "NTime", NTimeZeroTs },
+ { "Time", 0ll }
+ };
+ ZeroTs = -1;
+ for (const auto& p : timeZeros) {
+ if (paramName.EndsWith(p.first)) {
+ ZeroTs = p.second;
paramName.erase(paramName.size() - p.first.size());
- break;
- }
- }
+ break;
+ }
+ }
WWW_CHECK(ZeroTs != -1, "wrong special param name (postfix '*Time' required): %s", s.data());
- }
-
+ }
+
WWW_CHECK(paramName.empty(), "wrong special param name: %s", s.data());
- }
-
+ }
+
void ParseProbeSelector(const TString& s)
- {
- Probe = s;
- }
-
+ {
+ Probe = s;
+ }
+
void ParseProviderSelector(const TString& s)
- {
- Provider = s;
- }
-};
-
+ {
+ Provider = s;
+ }
+};
+
using TVariants = TVector<std::pair<TString, TString>>;
using TTags = TSet<TString>;
-
+
TString GetProbeName(const NLWTrace::TProbe* probe, const char* sep = ".")
-{
+{
return TString(probe->Event.GetProvider()) + sep + probe->Event.Name;
-}
-
-struct TAdHocTraceConfig {
- NLWTrace::TQuery Cfg;
-
- TAdHocTraceConfig() {} // Invalid config
-
- TAdHocTraceConfig(ui16 logSize, ui64 logDurationUs, bool logShuttle)
- {
- auto block = Cfg.AddBlocks(); // Create one block to distinguish valid config
- if (logSize) {
- Cfg.SetPerThreadLogSize(logSize);
- }
- if (logDurationUs) {
- Cfg.SetLogDurationUs(logDurationUs);
- }
- if (logShuttle) {
- block->AddAction()->MutableRunLogShuttleAction();
- }
- }
-
+}
+
+struct TAdHocTraceConfig {
+ NLWTrace::TQuery Cfg;
+
+ TAdHocTraceConfig() {} // Invalid config
+
+ TAdHocTraceConfig(ui16 logSize, ui64 logDurationUs, bool logShuttle)
+ {
+ auto block = Cfg.AddBlocks(); // Create one block to distinguish valid config
+ if (logSize) {
+ Cfg.SetPerThreadLogSize(logSize);
+ }
+ if (logDurationUs) {
+ Cfg.SetLogDurationUs(logDurationUs);
+ }
+ if (logShuttle) {
+ block->AddAction()->MutableRunLogShuttleAction();
+ }
+ }
+
TAdHocTraceConfig(const TString& provider, const TString& probe, ui16 logSize = 0, ui64 logDurationUs = 0, bool logShuttle = false)
- : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
- {
- auto block = Cfg.MutableBlocks(0);
- auto pdesc = block->MutableProbeDesc();
- pdesc->SetProvider(provider);
- pdesc->SetName(probe);
- }
-
+ : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
+ {
+ auto block = Cfg.MutableBlocks(0);
+ auto pdesc = block->MutableProbeDesc();
+ pdesc->SetProvider(provider);
+ pdesc->SetName(probe);
+ }
+
explicit TAdHocTraceConfig(const TString& group, ui16 logSize = 0, ui64 logDurationUs = 0, bool logShuttle = false)
- : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
- {
- auto block = Cfg.MutableBlocks(0);
- auto pdesc = block->MutableProbeDesc();
- pdesc->SetGroup(group);
- }
-
+ : TAdHocTraceConfig(logSize, logDurationUs, logShuttle)
+ {
+ auto block = Cfg.MutableBlocks(0);
+ auto pdesc = block->MutableProbeDesc();
+ pdesc->SetGroup(group);
+ }
+
TString Id() const
- {
- TStringStream ss;
- for (size_t blockIdx = 0; blockIdx < Cfg.BlocksSize(); blockIdx++) {
- if (!ss.Str().empty()) {
- ss << "/";
- }
- auto block = Cfg.GetBlocks(blockIdx);
- auto pdesc = block.GetProbeDesc();
+ {
+ TStringStream ss;
+ for (size_t blockIdx = 0; blockIdx < Cfg.BlocksSize(); blockIdx++) {
+ if (!ss.Str().empty()) {
+ ss << "/";
+ }
+ auto block = Cfg.GetBlocks(blockIdx);
+ auto pdesc = block.GetProbeDesc();
if (pdesc.GetProvider()) {
- ss << "." << pdesc.GetProvider() << "." << pdesc.GetName();
+ ss << "." << pdesc.GetProvider() << "." << pdesc.GetName();
} else if (pdesc.GetGroup()) {
- ss << ".Group." << pdesc.GetGroup();
- }
- // TODO[serxa]: handle predicate
- for (size_t actionIdx = 0; actionIdx < block.ActionSize(); actionIdx++) {
- const NLWTrace::TAction& action = block.GetAction(actionIdx);
- if (action.HasRunLogShuttleAction()) {
- auto ls = action.GetRunLogShuttleAction();
- ss << ".alsr";
- if (ls.GetIgnore()) {
- ss << "-i";
- }
+ ss << ".Group." << pdesc.GetGroup();
+ }
+ // TODO[serxa]: handle predicate
+ for (size_t actionIdx = 0; actionIdx < block.ActionSize(); actionIdx++) {
+ const NLWTrace::TAction& action = block.GetAction(actionIdx);
+ if (action.HasRunLogShuttleAction()) {
+ auto ls = action.GetRunLogShuttleAction();
+ ss << ".alsr";
+ if (ls.GetIgnore()) {
+ ss << "-i";
+ }
if (ls.GetShuttlesCount()) {
- ss << "-s" << ls.GetShuttlesCount();
- }
+ ss << "-s" << ls.GetShuttlesCount();
+ }
if (ls.GetMaxTrackLength()) {
- ss << "-t" << ls.GetMaxTrackLength();
- }
- } else if (action.HasEditLogShuttleAction()) {
- auto ls = action.GetEditLogShuttleAction();
- ss << ".alse";
- if (ls.GetIgnore()) {
- ss << "-i";
- }
- } else if (action.HasDropLogShuttleAction()) {
- ss << ".alsd";
- }
- }
- }
+ ss << "-t" << ls.GetMaxTrackLength();
+ }
+ } else if (action.HasEditLogShuttleAction()) {
+ auto ls = action.GetEditLogShuttleAction();
+ ss << ".alse";
+ if (ls.GetIgnore()) {
+ ss << "-i";
+ }
+ } else if (action.HasDropLogShuttleAction()) {
+ ss << ".alsd";
+ }
+ }
+ }
if (Cfg.GetPerThreadLogSize()) {
- ss << ".l" << Cfg.GetPerThreadLogSize();
- }
+ ss << ".l" << Cfg.GetPerThreadLogSize();
+ }
if (Cfg.GetLogDurationUs()) {
- ui64 logDurationUs = Cfg.GetLogDurationUs();
- if (logDurationUs % (60 * 1000 * 1000) == 0)
- ss << ".d" << logDurationUs / (60 * 1000 * 1000) << "m";
- if (logDurationUs % (1000 * 1000) == 0)
- ss << ".d" << logDurationUs / (1000 * 1000) << "s";
- else if (logDurationUs % 1000 == 0)
- ss << ".d" << logDurationUs / 1000 << "ms";
- else
- ss << ".d" << logDurationUs << "us";
- }
- return ss.Str();
- }
-
- bool IsValid() const
- {
- return Cfg.BlocksSize() > 0;
- }
-
- NLWTrace::TQuery Query() const
- {
- if (!IsValid()) {
- ythrow yexception() << "invalid adhoc trace config";
- }
- return Cfg;
- }
-
+ ui64 logDurationUs = Cfg.GetLogDurationUs();
+ if (logDurationUs % (60 * 1000 * 1000) == 0)
+ ss << ".d" << logDurationUs / (60 * 1000 * 1000) << "m";
+ if (logDurationUs % (1000 * 1000) == 0)
+ ss << ".d" << logDurationUs / (1000 * 1000) << "s";
+ else if (logDurationUs % 1000 == 0)
+ ss << ".d" << logDurationUs / 1000 << "ms";
+ else
+ ss << ".d" << logDurationUs << "us";
+ }
+ return ss.Str();
+ }
+
+ bool IsValid() const
+ {
+ return Cfg.BlocksSize() > 0;
+ }
+
+ NLWTrace::TQuery Query() const
+ {
+ if (!IsValid()) {
+ ythrow yexception() << "invalid adhoc trace config";
+ }
+ return Cfg;
+ }
+
bool ParseId(const TString& id)
- {
- if (IsAdHocId(id)) {
+ {
+ if (IsAdHocId(id)) {
for (const TString& block : SplitString(id, "/")) {
- if (block.empty()) {
- continue;
- }
- size_t cutPos = (block[0] == '.'? 1: 0);
+ if (block.empty()) {
+ continue;
+ }
+ size_t cutPos = (block[0] == '.'? 1: 0);
TVector<TString> parts = SplitString(block.substr(cutPos), ".");
WWW_CHECK(parts.size() >= 2, "too few parts in adhoc trace id '%s' block '%s'", id.data(), block.data());
- auto blockPb = Cfg.AddBlocks();
- auto pdescPb = blockPb->MutableProbeDesc();
- if (parts[0] == "Group") {
- pdescPb->SetGroup(parts[1]);
- } else {
- pdescPb->SetProvider(parts[0]);
- pdescPb->SetName(parts[1]);
- }
- bool defaultAction = true;
- for (auto i = parts.begin() + 2, e = parts.end(); i != e; ++i) {
+ auto blockPb = Cfg.AddBlocks();
+ auto pdescPb = blockPb->MutableProbeDesc();
+ if (parts[0] == "Group") {
+ pdescPb->SetGroup(parts[1]);
+ } else {
+ pdescPb->SetProvider(parts[0]);
+ pdescPb->SetName(parts[1]);
+ }
+ bool defaultAction = true;
+ for (auto i = parts.begin() + 2, e = parts.end(); i != e; ++i) {
const TString& part = *i;
- if (part.empty()) {
- continue;
- }
- switch (part[0]) {
- case 'l': Cfg.SetPerThreadLogSize(FromString<ui16>(part.substr(1))); break;
- case 'd': Cfg.SetLogDurationUs(ParseDuration(part.substr(1))); break;
- case 's': blockPb->MutablePredicate()->SetSampleRate(1.0 / Max<ui64>(1, FromString<ui64>(part.substr(1)))); break;
- case 'p': ParsePredicate(blockPb->MutablePredicate()->AddOperators(), part.substr(1)); break;
- case 'a': ParseAction(blockPb->AddAction(), part.substr(1)); defaultAction = false; break;
+ if (part.empty()) {
+ continue;
+ }
+ switch (part[0]) {
+ case 'l': Cfg.SetPerThreadLogSize(FromString<ui16>(part.substr(1))); break;
+ case 'd': Cfg.SetLogDurationUs(ParseDuration(part.substr(1))); break;
+ case 's': blockPb->MutablePredicate()->SetSampleRate(1.0 / Max<ui64>(1, FromString<ui64>(part.substr(1)))); break;
+ case 'p': ParsePredicate(blockPb->MutablePredicate()->AddOperators(), part.substr(1)); break;
+ case 'a': ParseAction(blockPb->AddAction(), part.substr(1)); defaultAction = false; break;
default: WWW_CHECK(false, "unknown adhoc trace part type '%s' in '%s'", part.data(), id.data());
- }
- }
- if (defaultAction) {
- blockPb->AddAction()->MutableLogAction();
- }
- }
- return true;
- }
- return false;
- }
-private:
+ }
+ }
+ if (defaultAction) {
+ blockPb->AddAction()->MutableLogAction();
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+private:
static bool IsAdHocId(const TString& id)
- {
- return !id.empty() && id[0] == '.';
- }
-
+ {
+ return !id.empty() && id[0] == '.';
+ }
+
void ParsePredicate(NLWTrace::TOperator* op, const TString& p)
- {
- size_t sign = p.find_first_of("=!><");
+ {
+ size_t sign = p.find_first_of("=!><");
WWW_CHECK(sign != TString::npos, "wrong predicate format in adhoc trace: %s", p.data());
- op->AddArgument()->SetParam(p.substr(0, sign));
- size_t value = sign + 1;
- switch (p[sign]) {
- case '=':
- op->SetType(NLWTrace::OT_EQ);
- break;
- case '!': {
+ op->AddArgument()->SetParam(p.substr(0, sign));
+ size_t value = sign + 1;
+ switch (p[sign]) {
+ case '=':
+ op->SetType(NLWTrace::OT_EQ);
+ break;
+ case '!': {
WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
WWW_CHECK(p[sign + 1] == '=', "wrong predicate operator format in adhoc trace: %s", p.data());
- value++;
- op->SetType(NLWTrace::OT_NE);
- break;
- }
- case '<': {
+ value++;
+ op->SetType(NLWTrace::OT_NE);
+ break;
+ }
+ case '<': {
WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
- if (p[sign + 1] == '=') {
- value++;
- op->SetType(NLWTrace::OT_LE);
- } else {
- op->SetType(NLWTrace::OT_LT);
- }
- break;
- }
- case '>': {
+ if (p[sign + 1] == '=') {
+ value++;
+ op->SetType(NLWTrace::OT_LE);
+ } else {
+ op->SetType(NLWTrace::OT_LT);
+ }
+ break;
+ }
+ case '>': {
WWW_CHECK(p.size() > sign + 1, "wrong predicate operator format in adhoc trace: %s", p.data());
- if (p[sign + 1] == '=') {
- value++;
- op->SetType(NLWTrace::OT_GE);
- } else {
- op->SetType(NLWTrace::OT_GT);
- }
- break;
- }
+ if (p[sign + 1] == '=') {
+ value++;
+ op->SetType(NLWTrace::OT_GE);
+ } else {
+ op->SetType(NLWTrace::OT_GT);
+ }
+ break;
+ }
default: WWW_CHECK(false, "wrong predicate operator format in adhoc trace: %s", p.data());
- }
- op->AddArgument()->SetValue(p.substr(value));
- }
-
+ }
+ op->AddArgument()->SetValue(p.substr(value));
+ }
+
void ParseAction(NLWTrace::TAction* action, const TString& a)
- {
- // NOTE: checks for longer action names should go first, your captain.
- if (a.substr(0, 3) == "lsr") {
- auto pb = action->MutableRunLogShuttleAction();
+ {
+ // NOTE: checks for longer action names should go first, your captain.
+ if (a.substr(0, 3) == "lsr") {
+ auto pb = action->MutableRunLogShuttleAction();
for (const TString& opt : SplitString(a.substr(3), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 'i': pb->SetIgnore(true); break;
- case 's': pb->SetShuttlesCount(FromString<ui64>(opt.substr(1))); break;
- case 't': pb->SetMaxTrackLength(FromString<ui64>(opt.substr(1))); break;
+ if (!opt.empty()) {
+ switch (opt[0]) {
+ case 'i': pb->SetIgnore(true); break;
+ case 's': pb->SetShuttlesCount(FromString<ui64>(opt.substr(1))); break;
+ case 't': pb->SetMaxTrackLength(FromString<ui64>(opt.substr(1))); break;
default: WWW_CHECK(false, "unknown adhoc trace log shuttle opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else if (a.substr(0, 3) == "lse") {
- auto pb = action->MutableEditLogShuttleAction();
+ }
+ }
+ }
+ } else if (a.substr(0, 3) == "lse") {
+ auto pb = action->MutableEditLogShuttleAction();
for (const TString& opt : SplitString(a.substr(3), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 'i': pb->SetIgnore(true); break;
+ if (!opt.empty()) {
+ switch (opt[0]) {
+ case 'i': pb->SetIgnore(true); break;
default: WWW_CHECK(false, "unknown adhoc trace log shuttle opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else if (a.substr(0, 3) == "lsd") {
- action->MutableDropLogShuttleAction();
- } else if (a.substr(0, 1) == "l") {
- auto pb = action->MutableLogAction();
+ }
+ }
+ }
+ } else if (a.substr(0, 3) == "lsd") {
+ action->MutableDropLogShuttleAction();
+ } else if (a.substr(0, 1) == "l") {
+ auto pb = action->MutableLogAction();
for (const TString& opt : SplitString(a.substr(1), "-")) {
- if (!opt.empty()) {
- switch (opt[0]) {
- case 't': pb->SetLogTimestamp(true); break;
- case 'r': pb->SetMaxRecords(FromString<ui32>(opt.substr(1))); break;
+ if (!opt.empty()) {
+ switch (opt[0]) {
+ case 't': pb->SetLogTimestamp(true); break;
+ case 'r': pb->SetMaxRecords(FromString<ui32>(opt.substr(1))); break;
default: WWW_CHECK(false, "unknown adhoc trace log opt '%s' in '%s'", opt.data(), a.data());
- }
- }
- }
- } else {
+ }
+ }
+ }
+ } else {
WWW_CHECK(false, "wrong action format in adhoc trace: %s", a.data());
- }
- }
-
+ }
+ }
+
static ui64 ParseDuration(const TString& s)
- {
- if (s.substr(s.length() - 2) == "us")
- return FromString<ui64>(s.substr(0, s.length() - 2));
- if (s.substr(s.length() - 2) == "ms")
- return FromString<ui64>(s.substr(0, s.length() - 2)) * 1000;
- if (s.substr(s.length() - 1) == "s")
- return FromString<ui64>(s.substr(0, s.length() - 1)) * 1000 * 1000;
- if (s.substr(s.length() - 1) == "m")
- return FromString<ui64>(s.substr(0, s.length() - 1)) * 60 * 1000 * 1000;
- else
- return FromString<ui64>(s);
- }
-};
-
-// Class that maintains one thread iff there are adhoc traces and cleans'em by deadlines
-class TTraceCleaner {
-private:
- NLWTrace::TManager* TraceMngr;
- TAtomic Quit = 0;
-
- TMutex Mtx;
- TCondVar WakeCondVar;
+ {
+ if (s.substr(s.length() - 2) == "us")
+ return FromString<ui64>(s.substr(0, s.length() - 2));
+ if (s.substr(s.length() - 2) == "ms")
+ return FromString<ui64>(s.substr(0, s.length() - 2)) * 1000;
+ if (s.substr(s.length() - 1) == "s")
+ return FromString<ui64>(s.substr(0, s.length() - 1)) * 1000 * 1000;
+ if (s.substr(s.length() - 1) == "m")
+ return FromString<ui64>(s.substr(0, s.length() - 1)) * 60 * 1000 * 1000;
+ else
+ return FromString<ui64>(s);
+ }
+};
+
+// Class that maintains one thread iff there are adhoc traces and cleans'em by deadlines
+class TTraceCleaner {
+private:
+ NLWTrace::TManager* TraceMngr;
+ TAtomic Quit = 0;
+
+ TMutex Mtx;
+ TCondVar WakeCondVar;
THashMap<TString, TInstant> Deadlines;
- volatile bool ThreadIsRunning = false;
- THolder<TThread> Thread;
-public:
- TTraceCleaner(NLWTrace::TManager* traceMngr)
- : TraceMngr(traceMngr)
- {}
-
- ~TTraceCleaner()
- {
- AtomicSet(Quit, 1);
- WakeCondVar.Signal();
- // TThread dtor joins thread
- }
-
- // Returns deadline for specified trace id or zero
+ volatile bool ThreadIsRunning = false;
+ THolder<TThread> Thread;
+public:
+ TTraceCleaner(NLWTrace::TManager* traceMngr)
+ : TraceMngr(traceMngr)
+ {}
+
+ ~TTraceCleaner()
+ {
+ AtomicSet(Quit, 1);
+ WakeCondVar.Signal();
+ // TThread dtor joins thread
+ }
+
+ // Returns deadline for specified trace id or zero
TInstant GetDeadline(const TString& id) const
- {
- TGuard<TMutex> g(Mtx);
- auto iter = Deadlines.find(id);
- return iter != Deadlines.end()? iter->second: TInstant::Zero();
- }
-
- // Postpone deletion of specified trace for specified timeout
+ {
+ TGuard<TMutex> g(Mtx);
+ auto iter = Deadlines.find(id);
+ return iter != Deadlines.end()? iter->second: TInstant::Zero();
+ }
+
+ // Postpone deletion of specified trace for specified timeout
void Postpone(const TString& id, TDuration timeout, bool allowLowering)
- {
- TGuard<TMutex> g(Mtx);
- TInstant newDeadline = TInstant::Now() + timeout;
- if (Deadlines[id] < newDeadline) {
- Deadlines[id] = newDeadline;
- } else if (allowLowering) { // Deadline lowering requires wake
- Deadlines[id] = newDeadline;
- WakeCondVar.Signal();
- }
- if (newDeadline != TInstant::Max() && !ThreadIsRunning) {
- // Note that dtor joins previous thread if any
- Thread.Reset(new TThread(ThreadProc, this));
- Thread->Start();
- ThreadIsRunning = true;
- }
- }
-
- // Forget about specified trace deletion
+ {
+ TGuard<TMutex> g(Mtx);
+ TInstant newDeadline = TInstant::Now() + timeout;
+ if (Deadlines[id] < newDeadline) {
+ Deadlines[id] = newDeadline;
+ } else if (allowLowering) { // Deadline lowering requires wake
+ Deadlines[id] = newDeadline;
+ WakeCondVar.Signal();
+ }
+ if (newDeadline != TInstant::Max() && !ThreadIsRunning) {
+ // Note that dtor joins previous thread if any
+ Thread.Reset(new TThread(ThreadProc, this));
+ Thread->Start();
+ ThreadIsRunning = true;
+ }
+ }
+
+ // Forget about specified trace deletion
void Forget(const TString& id)
- {
- TGuard<TMutex> g(Mtx);
- Deadlines.erase(id);
- WakeCondVar.Signal(); // in case thread is not required any more
- }
-private:
- void Exec()
- {
- while (!AtomicGet(Quit)) {
- TGuard<TMutex> g(Mtx);
-
- // Delete all timed out traces
- TInstant now = TInstant::Now();
- TInstant nextDeadline = TInstant::Max();
- for (auto i = Deadlines.begin(), e = Deadlines.end(); i != e;) {
+ {
+ TGuard<TMutex> g(Mtx);
+ Deadlines.erase(id);
+ WakeCondVar.Signal(); // in case thread is not required any more
+ }
+private:
+ void Exec()
+ {
+ while (!AtomicGet(Quit)) {
+ TGuard<TMutex> g(Mtx);
+
+ // Delete all timed out traces
+ TInstant now = TInstant::Now();
+ TInstant nextDeadline = TInstant::Max();
+ for (auto i = Deadlines.begin(), e = Deadlines.end(); i != e;) {
const TString& id = i->first;
- TInstant deadline = i->second;
- if (deadline < now) {
- try {
- TraceMngr->Delete(id);
- } catch (...) {
- // already deleted
- }
- Deadlines.erase(i++);
- } else {
- nextDeadline = Min(nextDeadline, deadline);
- ++i;
- }
- }
-
- // Stop thread if there is no more work
- if (Deadlines.empty() || nextDeadline == TInstant::Max()) {
- ThreadIsRunning = false;
- break;
- }
-
- // Wait until next deadline or quit
- WakeCondVar.WaitD(Mtx, nextDeadline);
- }
- }
-
- static void* ThreadProc(void* _this)
- {
+ TInstant deadline = i->second;
+ if (deadline < now) {
+ try {
+ TraceMngr->Delete(id);
+ } catch (...) {
+ // already deleted
+ }
+ Deadlines.erase(i++);
+ } else {
+ nextDeadline = Min(nextDeadline, deadline);
+ ++i;
+ }
+ }
+
+ // Stop thread if there is no more work
+ if (Deadlines.empty() || nextDeadline == TInstant::Max()) {
+ ThreadIsRunning = false;
+ break;
+ }
+
+ // Wait until next deadline or quit
+ WakeCondVar.WaitD(Mtx, nextDeadline);
+ }
+ }
+
+ static void* ThreadProc(void* _this)
+ {
TString name = "LWTraceCleaner";
- // Copy-pasted from kikimr/core/util/thread.h
-#if defined(_linux_)
- TStringStream linuxName;
- linuxName << TStringBuf(GetExecPath()).RNextTok('/') << "." << name;
+ // Copy-pasted from kikimr/core/util/thread.h
+#if defined(_linux_)
+ TStringStream linuxName;
+ linuxName << TStringBuf(GetExecPath()).RNextTok('/') << "." << name;
TThread::SetCurrentThreadName(linuxName.Str().data());
-#else
+#else
TThread::SetCurrentThreadName(name.data());
-#endif
- static_cast<TTraceCleaner*>(_this)->Exec();
- return nullptr;
- }
-};
-
-class TChromeTrace {
-private:
+#endif
+ static_cast<TTraceCleaner*>(_this)->Exec();
+ return nullptr;
+ }
+};
+
+class TChromeTrace {
+private:
TMultiMap<double, TString> TraceEvents;
THashMap<TThread::TId, TString> Tids;
-public:
+public:
void Add(TThread::TId tid, ui64 tsCycles, const TString& ph, const TString& cat,
- const NLWTrace::TLogItem* argsItem = nullptr,
+ const NLWTrace::TLogItem* argsItem = nullptr,
const TString& name = TString(), const TString& id = TString())
- {
- auto tidIter = Tids.find(tid);
- if (tidIter == Tids.end()) {
- tidIter = Tids.emplace(tid, ToString(Tids.size() + 1)).first;
- }
+ {
+ auto tidIter = Tids.find(tid);
+ if (tidIter == Tids.end()) {
+ tidIter = Tids.emplace(tid, ToString(Tids.size() + 1)).first;
+ }
const TString& shortId = tidIter->second;
- double ts = Timestamp(tsCycles);
- TraceEvents.emplace(ts, Event(shortId, ts, ph, cat, argsItem, name, id));
- }
-
+ double ts = Timestamp(tsCycles);
+ TraceEvents.emplace(ts, Event(shortId, ts, ph, cat, argsItem, name, id));
+ }
+
void Output(IOutputStream& os)
- {
- os << "{\"traceEvents\":[";
- bool first = true;
- for (auto kv : TraceEvents) {
- if (!first) {
- os << ",\n";
- }
- os << kv.second;
- first = false;
- }
- os << "]}";
- }
-
-private:
+ {
+ os << "{\"traceEvents\":[";
+ bool first = true;
+ for (auto kv : TraceEvents) {
+ if (!first) {
+ os << ",\n";
+ }
+ os << kv.second;
+ first = false;
+ }
+ os << "]}";
+ }
+
+private:
static TString Event(const TString& tid, double ts, const TString& ph, const TString& cat,
- const NLWTrace::TLogItem* argsItem,
+ const NLWTrace::TLogItem* argsItem,
const TString& name, const TString& id)
- {
- TStringStream ss;
- pid_t pid = 1;
- ss << "{\"pid\":" << pid
- << ",\"tid\":" << tid
- << ",\"ts\":" << Sprintf("%lf", ts)
- << ",\"ph\":\"" << ph << "\""
- << ",\"cat\":\"" << cat << "\"";
- if (name) {
- ss << ",\"name\":\"" << name << "\"";
- }
- if (id) {
- ss << ",\"id\":\"" << id << "\"";
- }
- if (argsItem && argsItem->SavedParamsCount > 0) {
- ss << ",\"args\":{";
+ {
+ TStringStream ss;
+ pid_t pid = 1;
+ ss << "{\"pid\":" << pid
+ << ",\"tid\":" << tid
+ << ",\"ts\":" << Sprintf("%lf", ts)
+ << ",\"ph\":\"" << ph << "\""
+ << ",\"cat\":\"" << cat << "\"";
+ if (name) {
+ ss << ",\"name\":\"" << name << "\"";
+ }
+ if (id) {
+ ss << ",\"id\":\"" << id << "\"";
+ }
+ if (argsItem && argsItem->SavedParamsCount > 0) {
+ ss << ",\"args\":{";
TString paramValues[LWTRACE_MAX_PARAMS];
- argsItem->Probe->Event.Signature.SerializeParams(argsItem->Params, paramValues);
- bool first = true;
- for (size_t pi = 0; pi < argsItem->SavedParamsCount; pi++, first = false) {
- if (!first) {
- ss << ",";
- }
+ argsItem->Probe->Event.Signature.SerializeParams(argsItem->Params, paramValues);
+ bool first = true;
+ for (size_t pi = 0; pi < argsItem->SavedParamsCount; pi++, first = false) {
+ if (!first) {
+ ss << ",";
+ }
ss << "\"" << TString(argsItem->Probe->Event.Signature.ParamNames[pi]) << "\":"
- "\"" << paramValues[pi] << "\"";
- }
- ss << "}";
- }
- ss << "}";
- return ss.Str();
- }
-
- static double Timestamp(ui64 cycles)
- {
- return double(cycles) * 1000000.0 / NHPTimer::GetClockRate();
- }
-};
-
-TString MakeUrl(const TCgiParameters& e, const THashMap<TString, TString>& values)
-{
- TStringStream ss;
- bool first = true;
- for (const auto& [k, v] : e) {
- if (values.find(k) == values.end()) {
- ss << (first? "?": "&") << k << "=" << v;
- first = false;
- }
- }
- for (const auto& [k, v] : values) {
- ss << (first? "?": "&") << k << "=" << v;
- first = false;
- }
- return ss.Str();
-}
-
+ "\"" << paramValues[pi] << "\"";
+ }
+ ss << "}";
+ }
+ ss << "}";
+ return ss.Str();
+ }
+
+ static double Timestamp(ui64 cycles)
+ {
+ return double(cycles) * 1000000.0 / NHPTimer::GetClockRate();
+ }
+};
+
+TString MakeUrl(const TCgiParameters& e, const THashMap<TString, TString>& values)
+{
+ TStringStream ss;
+ bool first = true;
+ for (const auto& [k, v] : e) {
+ if (values.find(k) == values.end()) {
+ ss << (first? "?": "&") << k << "=" << v;
+ first = false;
+ }
+ }
+ for (const auto& [k, v] : values) {
+ ss << (first? "?": "&") << k << "=" << v;
+ first = false;
+ }
+ return ss.Str();
+}
+
TString MakeUrl(const TCgiParameters& e, const TString& key, const TString& value, bool keep = false)
-{
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- if (keep || kv.first != key) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- ss << (first? "?": "&") << key << "=" << value;
- return ss.Str();
-}
-
+{
+ TStringStream ss;
+ bool first = true;
+ for (const auto& kv : e) {
+ if (keep || kv.first != key) {
+ ss << (first? "?": "&") << kv.first << "=" << kv.second;
+ first = false;
+ }
+ }
+ ss << (first? "?": "&") << key << "=" << value;
+ return ss.Str();
+}
+
TString MakeUrlAdd(const TCgiParameters& e, const TString& key, const TString& value)
-{
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- ss << (first? "?": "&") << key << "=" << value;
- return ss.Str();
-}
-
+{
+ TStringStream ss;
+ bool first = true;
+ for (const auto& kv : e) {
+ ss << (first? "?": "&") << kv.first << "=" << kv.second;
+ first = false;
+ }
+ ss << (first? "?": "&") << key << "=" << value;
+ return ss.Str();
+}
+
TString MakeUrlReplace(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue)
-{
- TStringStream ss;
- bool first = true;
- bool inserted = false;
- for (const auto& kv : e) {
- if (kv.first == key && (kv.second == oldValue || kv.second == newValue)) {
- if (!inserted) {
- inserted = true;
- ss << (first? "?": "&") << key << "=" << newValue;
- first = false;
- }
- } else {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- if (!inserted) {
- ss << (first? "?": "&") << key << "=" << newValue;
- }
- return ss.Str();
-}
-
+{
+ TStringStream ss;
+ bool first = true;
+ bool inserted = false;
+ for (const auto& kv : e) {
+ if (kv.first == key && (kv.second == oldValue || kv.second == newValue)) {
+ if (!inserted) {
+ inserted = true;
+ ss << (first? "?": "&") << key << "=" << newValue;
+ first = false;
+ }
+ } else {
+ ss << (first? "?": "&") << kv.first << "=" << kv.second;
+ first = false;
+ }
+ }
+ if (!inserted) {
+ ss << (first? "?": "&") << key << "=" << newValue;
+ }
+ return ss.Str();
+}
+
TString MakeUrlErase(const TCgiParameters& e, const TString& key, const TString& value)
-{
- TStringStream ss;
- bool first = true;
- for (const auto& kv : e) {
- if (kv.first != key || kv.second != value) {
- ss << (first? "?": "&") << kv.first << "=" << kv.second;
- first = false;
- }
- }
- return ss.Str();
-}
-
+{
+ TStringStream ss;
+ bool first = true;
+ for (const auto& kv : e) {
+ if (kv.first != key || kv.second != value) {
+ ss << (first? "?": "&") << kv.first << "=" << kv.second;
+ first = false;
+ }
+ }
+ return ss.Str();
+}
+
TString EscapeSubvalue(const TString& s)
-{
+{
TString ret;
ret.reserve(s.size());
for (size_t i = 0; i < s.size(); i++) {
- char c = s[i];
- if (c == ':') {
- ret.append("^c");
- } else if (c == '^') {
- ret.append("^^");
- } else {
- ret.append(c);
- }
- }
- return ret;
-}
-
+ char c = s[i];
+ if (c == ':') {
+ ret.append("^c");
+ } else if (c == '^') {
+ ret.append("^^");
+ } else {
+ ret.append(c);
+ }
+ }
+ return ret;
+}
+
TString UnescapeSubvalue(const TString& s)
-{
+{
TString ret;
ret.reserve(s.size());
for (size_t i = 0; i < s.size(); i++) {
- char c = s[i];
+ char c = s[i];
if (c == '^' && i + 1 < s.size()) {
- char c2 = s[++i];
- if (c2 == 'c') {
- ret.append(':');
- } else if (c2 == '^') {
- ret.append('^');
- } else {
- ret.append(c);
- ret.append(c2);
- }
- } else {
- ret.append(c);
- }
- }
- return ret;
-}
-
+ char c2 = s[++i];
+ if (c2 == 'c') {
+ ret.append(':');
+ } else if (c2 == '^') {
+ ret.append('^');
+ } else {
+ ret.append(c);
+ ret.append(c2);
+ }
+ } else {
+ ret.append(c);
+ }
+ }
+ return ret;
+}
+
TVector<TString> Subvalues(const TCgiParameters& e, const TString& key)
-{
- if (!e.Has(key)) {
+{
+ if (!e.Has(key)) {
return TVector<TString>();
- } else {
+ } else {
TVector<TString> ret;
for (const TString& s : SplitString(e.Get(key), ":", 0, KEEP_EMPTY_TOKENS)) {
- ret.push_back(UnescapeSubvalue(s));
- }
- if (ret.empty()) {
- ret.push_back("");
- }
- return ret;
- }
-}
-
+ ret.push_back(UnescapeSubvalue(s));
+ }
+ if (ret.empty()) {
+ ret.push_back("");
+ }
+ return ret;
+ }
+}
+
TString ParseTagsOut(const TString& taggedStr, TTags& tags)
-{
+{
auto vec = SplitString(taggedStr, "-");
- if (vec.empty()) {
- return "";
- }
- auto iter = vec.begin();
+ if (vec.empty()) {
+ return "";
+ }
+ auto iter = vec.begin();
TString value = *iter++;
- for (;iter != vec.end(); ++iter) {
- tags.insert(*iter);
- }
- return value;
-}
-
+ for (;iter != vec.end(); ++iter) {
+ tags.insert(*iter);
+ }
+ return value;
+}
+
TString JoinTags(TTags tags) {
return JoinStrings(TVector<TString>(tags.begin(), tags.end()), "-");
-}
-
+}
+
TString MakeValue(const TVector<TString>& subvalues)
-{
+{
TVector<TString> subvaluesEsc;
for (const TString& s : subvalues) {
- subvaluesEsc.push_back(EscapeSubvalue(s));
- }
+ subvaluesEsc.push_back(EscapeSubvalue(s));
+ }
return JoinStrings(subvaluesEsc, ":");
-}
-
+}
+
TString MakeUrlAddSub(const TCgiParameters& e, const TString& key, const TString& subvalue)
-{
+{
const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- subvalues.push_back(subvalue);
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
-}
-
+ auto subvalues = Subvalues(e, key);
+ subvalues.push_back(subvalue);
+ return MakeUrlReplace(e, key, value, MakeValue(subvalues));
+}
+
TString MakeUrlReplaceSub(const TCgiParameters& e, const TString& key, const TString& oldSubvalue, const TString& newSubvalue)
-{
+{
const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- auto iter = std::find(subvalues.begin(), subvalues.end(), oldSubvalue);
- if (iter != subvalues.end()) {
- *iter = newSubvalue;
- } else {
- subvalues.push_back(newSubvalue);
- }
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
-}
-
+ auto subvalues = Subvalues(e, key);
+ auto iter = std::find(subvalues.begin(), subvalues.end(), oldSubvalue);
+ if (iter != subvalues.end()) {
+ *iter = newSubvalue;
+ } else {
+ subvalues.push_back(newSubvalue);
+ }
+ return MakeUrlReplace(e, key, value, MakeValue(subvalues));
+}
+
TString MakeUrlEraseSub(const TCgiParameters& e, const TString& key, const TString& subvalue)
-{
+{
const TString& value = e.Get(key);
- auto subvalues = Subvalues(e, key);
- auto iter = std::find(subvalues.begin(), subvalues.end(), subvalue);
- if (iter != subvalues.end()) {
- subvalues.erase(iter);
- }
- if (subvalues.empty()) {
- return MakeUrlErase(e, key, value);
- } else {
- return MakeUrlReplace(e, key, value, MakeValue(subvalues));
- }
-}
-
+ auto subvalues = Subvalues(e, key);
+ auto iter = std::find(subvalues.begin(), subvalues.end(), subvalue);
+ if (iter != subvalues.end()) {
+ subvalues.erase(iter);
+ }
+ if (subvalues.empty()) {
+ return MakeUrlErase(e, key, value);
+ } else {
+ return MakeUrlReplace(e, key, value, MakeValue(subvalues));
+ }
+}
+
template <bool sub> TString UrlAdd(const TCgiParameters& e, const TString& key, const TString& value);
template <> TString UrlAdd<false>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlAdd(e, key, value);
-}
+ return MakeUrlAdd(e, key, value);
+}
template <> TString UrlAdd<true>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlAddSub(e, key, value);
-}
-
+ return MakeUrlAddSub(e, key, value);
+}
+
template <bool sub> TString UrlReplace(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue);
template <> TString UrlReplace<false>(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue) {
- return MakeUrlReplace(e, key, oldValue, newValue);
-}
+ return MakeUrlReplace(e, key, oldValue, newValue);
+}
template <> TString UrlReplace<true>(const TCgiParameters& e, const TString& key, const TString& oldValue, const TString& newValue) {
- return MakeUrlReplaceSub(e, key, oldValue, newValue);
-}
-
+ return MakeUrlReplaceSub(e, key, oldValue, newValue);
+}
+
template <bool sub> TString UrlErase(const TCgiParameters& e, const TString& key, const TString& value);
template <> TString UrlErase<false>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlErase(e, key, value);
-}
+ return MakeUrlErase(e, key, value);
+}
template <> TString UrlErase<true>(const TCgiParameters& e, const TString& key, const TString& value) {
- return MakeUrlEraseSub(e, key, value);
-}
-
+ return MakeUrlEraseSub(e, key, value);
+}
+
void OutputCommonHeader(IOutputStream& out)
-{
- out << NResource::Find("lwtrace/mon/static/header.html") << Endl;
-}
-
+{
+ out << NResource::Find("lwtrace/mon/static/header.html") << Endl;
+}
+
void OutputCommonFooter(IOutputStream& out)
-{
- out << NResource::Find("lwtrace/mon/static/footer.html") << Endl;
-}
-
-struct TScopedHtmlInner {
+{
+ out << NResource::Find("lwtrace/mon/static/footer.html") << Endl;
+}
+
+struct TScopedHtmlInner {
explicit TScopedHtmlInner(IOutputStream& str)
- : Str(str)
- {
- Str << "<!DOCTYPE html>\n"
- "<html>";
- HTML(str) {
- HEAD() { OutputCommonHeader(Str); }
- }
- Str << "<body>";
- }
-
- ~TScopedHtmlInner()
- {
- OutputCommonFooter(Str);
- Str << "</body></html>";
- }
-
- inline operator bool () const noexcept { return true; }
-
+ : Str(str)
+ {
+ Str << "<!DOCTYPE html>\n"
+ "<html>";
+ HTML(str) {
+ HEAD() { OutputCommonHeader(Str); }
+ }
+ Str << "<body>";
+ }
+
+ ~TScopedHtmlInner()
+ {
+ OutputCommonFooter(Str);
+ Str << "</body></html>";
+ }
+
+ inline operator bool () const noexcept { return true; }
+
IOutputStream &Str;
-};
-
+};
+
TString NavbarHeader()
-{
- return "<div class=\"navbar-header\">"
- "<a class=\"navbar-brand\" href=\"?mode=\">LWTrace</a>"
- "</div>";
-}
-
-struct TSelectorsContainer {
+{
+ return "<div class=\"navbar-header\">"
+ "<a class=\"navbar-brand\" href=\"?mode=\">LWTrace</a>"
+ "</div>";
+}
+
+struct TSelectorsContainer {
TSelectorsContainer(IOutputStream& str)
- : Str(str)
- {
- Str << "<nav id=\"selectors-container\" class=\"navbar navbar-default\">"
- "<div class=\"container-fluid\">"
- << NavbarHeader() <<
- "<div class=\"navbar-text\" style=\"margin-top:12px;margin-bottom:10px\">";
- }
-
- ~TSelectorsContainer() {
- try {
- Str <<
- "</div>"
- "<div class=\"container-fluid\">"
- "<div class=\"pull-right\">"
- "<button id=\"download-btn\""
- " type=\"button\" style=\"display: inline-block;margin:7px\""
- " title=\"Chromium trace (load it in chrome://tracing/)\""
- " class=\"btn btn-default hidden\">"
- "<span class=\"glyphicon glyphicon-download-alt\"></span>"
- "</button>"
- "</div>"
- "</div>"
- "</div></nav>";
- } catch(...) {}
- }
-
+ : Str(str)
+ {
+ Str << "<nav id=\"selectors-container\" class=\"navbar navbar-default\">"
+ "<div class=\"container-fluid\">"
+ << NavbarHeader() <<
+ "<div class=\"navbar-text\" style=\"margin-top:12px;margin-bottom:10px\">";
+ }
+
+ ~TSelectorsContainer() {
+ try {
+ Str <<
+ "</div>"
+ "<div class=\"container-fluid\">"
+ "<div class=\"pull-right\">"
+ "<button id=\"download-btn\""
+ " type=\"button\" style=\"display: inline-block;margin:7px\""
+ " title=\"Chromium trace (load it in chrome://tracing/)\""
+ " class=\"btn btn-default hidden\">"
+ "<span class=\"glyphicon glyphicon-download-alt\"></span>"
+ "</button>"
+ "</div>"
+ "</div>"
+ "</div></nav>";
+ } catch(...) {}
+ }
+
IOutputStream& Str;
-};
-
-struct TNullContainer {
+};
+
+struct TNullContainer {
TNullContainer(IOutputStream&) {}
-};
-
-class TPageGenBase: public std::exception {};
-template <class TContainer = TNullContainer>
-class TPageGen: public TPageGenBase {
-private:
+};
+
+class TPageGenBase: public std::exception {};
+template <class TContainer = TNullContainer>
+class TPageGen: public TPageGenBase {
+private:
TString Content;
TString HttpResponse;
-public:
- void BuildResponse()
- {
- TStringStream ss;
- WWW_HTML(ss) {
- TContainer container(ss);
- ss << Content;
- }
- HttpResponse = ss.Str();
- }
-
+public:
+ void BuildResponse()
+ {
+ TStringStream ss;
+ WWW_HTML(ss) {
+ TContainer container(ss);
+ ss << Content;
+ }
+ HttpResponse = ss.Str();
+ }
+
explicit TPageGen(const TString& content = TString())
- : Content(content)
- {
- BuildResponse();
- }
-
+ : Content(content)
+ {
+ BuildResponse();
+ }
+
void Append(const TString& moreContent)
- {
- Content.append(moreContent);
- BuildResponse();
- }
-
+ {
+ Content.append(moreContent);
+ BuildResponse();
+ }
+
void Prepend(const TString& moreContent)
- {
- Content.prepend(moreContent);
- BuildResponse();
- }
-
+ {
+ Content.prepend(moreContent);
+ BuildResponse();
+ }
+
virtual const char* what() const noexcept { return HttpResponse.data(); }
operator bool() const { return !Content.empty(); }
-};
-
-enum EStyleFlags {
- // bit 1
- Link = 0x0,
- Button = 0x1,
-
- // bit 2
- NonErasable = 0x0,
- Erasable = 0x2,
-
- // bit 3-4
- Medium = 0x0,
- Large = 0x4,
- Small = 0x8,
- ExtraSmall = 0xC,
- SizeMask = 0xC,
-
- // bit 5
- NoCaret = 0x0,
- Caret = 0x10,
-
- // bit 6
- SimpleValue = 0x0,
- CompositeValue = 0x20
-};
-
-template <ui64 flags>
+};
+
+enum EStyleFlags {
+ // bit 1
+ Link = 0x0,
+ Button = 0x1,
+
+ // bit 2
+ NonErasable = 0x0,
+ Erasable = 0x2,
+
+ // bit 3-4
+ Medium = 0x0,
+ Large = 0x4,
+ Small = 0x8,
+ ExtraSmall = 0xC,
+ SizeMask = 0xC,
+
+ // bit 5
+ NoCaret = 0x0,
+ Caret = 0x10,
+
+ // bit 6
+ SimpleValue = 0x0,
+ CompositeValue = 0x20
+};
+
+template <ui64 flags>
TString BtnClass() {
- if ((flags & SizeMask) == Large) {
- return "btn btn-lg";
- } else if ((flags & SizeMask) == Small) {
- return "btn btn-sm";
- } else if ((flags & SizeMask) == ExtraSmall) {
- return "btn btn-xs";
- }
- return "btn";
-}
-
+ if ((flags & SizeMask) == Large) {
+ return "btn btn-lg";
+ } else if ((flags & SizeMask) == Small) {
+ return "btn btn-sm";
+ } else if ((flags & SizeMask) == ExtraSmall) {
+ return "btn btn-xs";
+ }
+ return "btn";
+}
+
void SelectorTitle(IOutputStream& os, const TString& text)
-{
- if (!text.empty()) {
- os << text;
- }
-}
-
-template <ui64 flags>
+{
+ if (!text.empty()) {
+ os << text;
+ }
+}
+
+template <ui64 flags>
void BtnHref(IOutputStream& os, const TString& text, const TString& href, bool push = false)
-{
- if (flags & Button) {
- os << "<button type=\"button\" style=\"display: inline-block;margin:3px\" class=\""
- << BtnClass<flags>() << " "
- << (push? "btn-primary": "btn-default")
- << "\" onClick=\"window.location.href='" << href << "';\">"
- << text
- << "</button>";
- } else {
- os << "<a href=\"" << href << "\">"
- << text
- << "</a>";
- }
-}
-
+{
+ if (flags & Button) {
+ os << "<button type=\"button\" style=\"display: inline-block;margin:3px\" class=\""
+ << BtnClass<flags>() << " "
+ << (push? "btn-primary": "btn-default")
+ << "\" onClick=\"window.location.href='" << href << "';\">"
+ << text
+ << "</button>";
+ } else {
+ os << "<a href=\"" << href << "\">"
+ << text
+ << "</a>";
+ }
+}
+
void DropdownBeginSublist(IOutputStream& os, const TString& text)
-{
- os << "<li>" << text << "<ul class=\"dropdown-menu\">";
-}
-
+{
+ os << "<li>" << text << "<ul class=\"dropdown-menu\">";
+}
+
void DropdownEndSublist(IOutputStream& os)
-{
- os << "</ul></li>";
-}
-
+{
+ os << "</ul></li>";
+}
+
void DropdownItem(IOutputStream& os, const TString& text, const TString& href, bool separated = false)
-{
- if (separated) {
- os << "<li role=\"separator\" class=\"divider\"></li>";
- }
- os << "<li><a href=\"" << href << "\">" << text << "</a></li>";
-}
-
+{
+ if (separated) {
+ os << "<li role=\"separator\" class=\"divider\"></li>";
+ }
+ os << "<li><a href=\"" << href << "\">" << text << "</a></li>";
+}
+
TString SuggestSelection()
-{
- return "--- ";
-}
-
+{
+ return "--- ";
+}
+
TString RemoveSelection()
-{
- return "Remove";
-}
-
+{
+ return "Remove";
+}
+
TString GetDescription(const TString& value, const TVariants& variants)
-{
- for (const auto& var : variants) {
- if (value == var.first) {
- return var.second;
- }
- }
- if (!value) {
- return SuggestSelection();
- }
- return value;
-}
-
-template <ui64 flags, bool sub = false>
+{
+ for (const auto& var : variants) {
+ if (value == var.first) {
+ return var.second;
+ }
+ }
+ if (!value) {
+ return SuggestSelection();
+ }
+ return value;
+}
+
+template <ui64 flags, bool sub = false>
void DropdownSelector(IOutputStream& os, const TCgiParameters& e, const TString& param, const TString& value,
const TString& text, const TVariants& variants, const TString& realValue = TString())
-{
- HTML(os) {
- SelectorTitle(os, text);
- os << "<div class=\"dropdown\" style=\"display:inline-block;margin:3px\">";
- if (flags & Button) {
- os << "<button class=\"" << BtnClass<flags>() << " btn-primary dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\">";
- } else {
- os << "<a href=\"#\" data-toggle=\"dropdown\">";
- }
- os << GetDescription(flags & CompositeValue? realValue: value, variants);
- if (flags & Caret) {
- os << "<span class=\"caret\"></span>";
- }
- if (flags & Button) {
- os <<"</button>";
- } else {
- os <<"</a>";
- }
- UL_CLASS ("dropdown-menu") {
- for (const auto& var : variants) {
- DropdownItem(os, var.second, UrlReplace<sub>(e, param, value, var.first));
- }
- if (flags & Erasable) {
- DropdownItem(os, RemoveSelection(), UrlErase<sub>(e, param, value), true);
- }
- }
- os << "</div>";
- }
-}
-
+{
+ HTML(os) {
+ SelectorTitle(os, text);
+ os << "<div class=\"dropdown\" style=\"display:inline-block;margin:3px\">";
+ if (flags & Button) {
+ os << "<button class=\"" << BtnClass<flags>() << " btn-primary dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\">";
+ } else {
+ os << "<a href=\"#\" data-toggle=\"dropdown\">";
+ }
+ os << GetDescription(flags & CompositeValue? realValue: value, variants);
+ if (flags & Caret) {
+ os << "<span class=\"caret\"></span>";
+ }
+ if (flags & Button) {
+ os <<"</button>";
+ } else {
+ os <<"</a>";
+ }
+ UL_CLASS ("dropdown-menu") {
+ for (const auto& var : variants) {
+ DropdownItem(os, var.second, UrlReplace<sub>(e, param, value, var.first));
+ }
+ if (flags & Erasable) {
+ DropdownItem(os, RemoveSelection(), UrlErase<sub>(e, param, value), true);
+ }
+ }
+ os << "</div>";
+ }
+}
+
void RequireSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
const TString& text, const TVariants& variants)
-{
+{
const TString& value = e.Get(param);
- DropdownSelector<Link>(ss, e, param, value, text, variants);
- if (!value) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
-}
-
+ DropdownSelector<Link>(ss, e, param, value, text, variants);
+ if (!value) {
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ }
+}
+
void RequireMultipleSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
const TString& text, const TVariants& variants)
-{
- SelectorTitle(ss, text);
+{
+ SelectorTitle(ss, text);
TSet<TString> selectedValues;
for (const TString& subvalue : Subvalues(e, param)) {
- selectedValues.insert(subvalue);
- }
+ selectedValues.insert(subvalue);
+ }
for (const TString& subvalue : Subvalues(e, param)) {
- DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
- }
+ DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
+ }
if (selectedValues.contains("")) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, param, ""));
- if (selectedValues.empty()) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
- }
-}
-
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ } else {
+ BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, param, ""));
+ if (selectedValues.empty()) {
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ }
+ }
+}
+
void OptionalSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
const TString& text, const TVariants& variants)
{
@@ -1424,251 +1424,251 @@ void OptionalSelection(TStringStream& ss, const TCgiParameters& e, const TString
void OptionalMultipleSelection(TStringStream& ss, const TCgiParameters& e, const TString& param,
const TString& text, const TVariants& variants)
-{
+{
TSet<TString> selectedValues;
for (const TString& subvalue : Subvalues(e, param)) {
- selectedValues.insert(subvalue);
- }
- if (!selectedValues.empty()) {
- SelectorTitle(ss, text);
- }
+ selectedValues.insert(subvalue);
+ }
+ if (!selectedValues.empty()) {
+ SelectorTitle(ss, text);
+ }
for (const TString& subvalue : Subvalues(e, param)) {
- DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
- }
+ DropdownSelector<Erasable, true>(ss, e, param, subvalue, "", variants);
+ }
if (selectedValues.contains("")) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, selectedValues.empty()? text: "+", MakeUrlAddSub(e, param, ""));
- }
-}
-
-TVariants ListColumns(const NAnalytics::TTable& table)
-{
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ } else {
+ BtnHref<Button|ExtraSmall>(ss, selectedValues.empty()? text: "+", MakeUrlAddSub(e, param, ""));
+ }
+}
+
+TVariants ListColumns(const NAnalytics::TTable& table)
+{
TSet<TString> cols;
-// bool addSpecialCols = false;
-// if (addSpecialCols) {
-// cols.insert("_count");
-// }
- for (auto& row : table) {
- for (auto& kv : row) {
- cols.insert(kv.first);
- }
- }
- TVariants result;
- for (const auto& s : cols) {
- result.emplace_back(s, s);
- }
- return result;
-}
-
+// bool addSpecialCols = false;
+// if (addSpecialCols) {
+// cols.insert("_count");
+// }
+ for (auto& row : table) {
+ for (auto& kv : row) {
+ cols.insert(kv.first);
+ }
+ }
+ TVariants result;
+ for (const auto& s : cols) {
+ result.emplace_back(s, s);
+ }
+ return result;
+}
+
TString TaggedValue(const TString& value, const TString& tag)
-{
- if (!tag) {
- return value;
- }
- return value + "-" + tag;
-}
-
+{
+ if (!tag) {
+ return value;
+ }
+ return value + "-" + tag;
+}
+
TVariants ValueVars(const TVariants& values, const TString& tag)
-{
- TVariants ret;
- for (auto& p : values) {
- ret.emplace_back(TaggedValue(p.first, tag), p.second);
- }
- return ret;
-}
-
+{
+ TVariants ret;
+ for (auto& p : values) {
+ ret.emplace_back(TaggedValue(p.first, tag), p.second);
+ }
+ return ret;
+}
+
TVariants TagVars(const TString& value, const TVariants& tags)
-{
- TVariants ret;
- for (auto& p : tags) {
- ret.emplace_back(TaggedValue(value, p.first), p.second);
- }
- return ret;
-}
-
-TVariants SeriesTags()
-{
- TVariants ret; // MSVS2013 doesn't understand complex initializer lists
- ret.emplace_back("", "as is");
- ret.emplace_back("stack", "cumulative");
- return ret;
-}
-
-void SeriesSelectors(TStringStream& ss, const TCgiParameters& e,
+{
+ TVariants ret;
+ for (auto& p : tags) {
+ ret.emplace_back(TaggedValue(value, p.first), p.second);
+ }
+ return ret;
+}
+
+TVariants SeriesTags()
+{
+ TVariants ret; // MSVS2013 doesn't understand complex initializer lists
+ ret.emplace_back("", "as is");
+ ret.emplace_back("stack", "cumulative");
+ return ret;
+}
+
+void SeriesSelectors(TStringStream& ss, const TCgiParameters& e,
const TString& xparam, const TString& yparam, const NAnalytics::TTable& data)
-{
- TTags xtags;
+{
+ TTags xtags;
TString xn = ParseTagsOut(e.Get(xparam), xtags);
- DropdownSelector<Erasable, true>(ss, e, xparam, e.Get(xparam), "with Ox:",
- ValueVars(ListColumns(data), JoinTags(xtags)));
- if (xn) {
- DropdownSelector<Link, true>(ss, e, xparam, e.Get(xparam), "",
- TagVars(xn, SeriesTags()));
- }
-
+ DropdownSelector<Erasable, true>(ss, e, xparam, e.Get(xparam), "with Ox:",
+ ValueVars(ListColumns(data), JoinTags(xtags)));
+ if (xn) {
+ DropdownSelector<Link, true>(ss, e, xparam, e.Get(xparam), "",
+ TagVars(xn, SeriesTags()));
+ }
+
TString yns = e.Get(yparam);
- SelectorTitle(ss, "and Oy:");
- bool first = true;
- bool hasEmpty = false;
- for (auto& subvalue : Subvalues(e, yparam)) {
- TTags ytags;
+ SelectorTitle(ss, "and Oy:");
+ bool first = true;
+ bool hasEmpty = false;
+ for (auto& subvalue : Subvalues(e, yparam)) {
+ TTags ytags;
TString yn = ParseTagsOut(subvalue, ytags);
- DropdownSelector<Erasable, true>(ss, e, yparam, subvalue, first? "": ", ",
- ValueVars(ListColumns(data), JoinTags(ytags)));
- if (yn) {
- DropdownSelector<Link, true>(ss, e, yparam, subvalue, "",
- TagVars(yn, SeriesTags()));
- }
- first = false;
- if (yn.empty()) {
- hasEmpty = true;
- }
- }
-
- if (hasEmpty) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, yparam, ""));
- }
-
- if (!xn || !yns) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- }
-}
-
+ DropdownSelector<Erasable, true>(ss, e, yparam, subvalue, first? "": ", ",
+ ValueVars(ListColumns(data), JoinTags(ytags)));
+ if (yn) {
+ DropdownSelector<Link, true>(ss, e, yparam, subvalue, "",
+ TagVars(yn, SeriesTags()));
+ }
+ first = false;
+ if (yn.empty()) {
+ hasEmpty = true;
+ }
+ }
+
+ if (hasEmpty) {
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ } else {
+ BtnHref<Button|ExtraSmall>(ss, "+", MakeUrlAddSub(e, yparam, ""));
+ }
+
+ if (!xn || !yns) {
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ }
+}
+
class TProbesHtmlPrinter {
private:
TVector<TVector<TString>> TableData;
- static constexpr int TimeoutSec = 15 * 60; // default timeout
+ static constexpr int TimeoutSec = 15 * 60; // default timeout
public:
void Push(const NLWTrace::TProbe* probe)
{
TableData.emplace_back();
- auto& row = TableData.back();
-
+ auto& row = TableData.back();
+
row.emplace_back();
TString& groups = row.back();
bool first = true;
for (const char* const* i = probe->Event.Groups; *i != nullptr; ++i, first = false) {
groups.append(TString(first? "": ", ") + GroupHtml(*i));
}
-
- row.push_back(ProbeHtml(probe->Event.GetProvider(), probe->Event.Name));
-
+
+ row.push_back(ProbeHtml(probe->Event.GetProvider(), probe->Event.Name));
+
row.emplace_back();
TString& params = row.back();
first = true;
for (size_t i = 0; i < probe->Event.Signature.ParamCount; i++, first = false) {
params.append(TString(first? "": ", ") + probe->Event.Signature.ParamTypes[i]
- + " " + probe->Event.Signature.ParamNames[i]);
+ + " " + probe->Event.Signature.ParamNames[i]);
}
-
- row.emplace_back(ToString(probe->GetExecutorsCount()));
+
+ row.emplace_back(ToString(probe->GetExecutorsCount()));
}
-
+
void Output(IOutputStream& os)
- {
- HTML(os) {
- TABLE() {
- TABLEHEAD() {
- TABLEH() { os << "Groups"; }
- TABLEH() { os << "Name"; }
- TABLEH() { os << "Params"; }
- TABLEH() { os << "ExecCount"; }
- }
- TABLEBODY() {
- for (auto& row : TableData) {
- TABLER() {
+ {
+ HTML(os) {
+ TABLE() {
+ TABLEHEAD() {
+ TABLEH() { os << "Groups"; }
+ TABLEH() { os << "Name"; }
+ TABLEH() { os << "Params"; }
+ TABLEH() { os << "ExecCount"; }
+ }
+ TABLEBODY() {
+ for (auto& row : TableData) {
+ TABLER() {
for (TString& cell : row) {
- TABLED() { os << cell; }
- }
- }
- }
- }
- }
- }
- }
-private:
+ TABLED() { os << cell; }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+private:
TString GroupHtml(const TString& group)
- {
- TStringStream ss;
- ss << "<div class=\"dropdown\" style=\"display:inline-block\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << group << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group).Id() << "'});\">"
- "Trace 1000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 10000).Id() << "'});\">"
- "Trace 10000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 1000000).Id() << "'});\">"
- "Trace 1 second</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 10000000).Id() << "'});\">"
- "Trace 10 seconds</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 0, 0, true).Id() << "'});\">"
- "Trace 1000 tracks</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(group, 10000, 0, true).Id() << "'});\">"
- "Trace 10000 tracks</a></li>"
- "</ul>"
- "</div>";
- return ss.Str();
- }
-
+ {
+ TStringStream ss;
+ ss << "<div class=\"dropdown\" style=\"display:inline-block\">"
+ "<a href=\"#\" data-toggle=\"dropdown\">" << group << "</a>"
+ "<ul class=\"dropdown-menu\">"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group).Id() << "'});\">"
+ "Trace 1000 items</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group, 10000).Id() << "'});\">"
+ "Trace 10000 items</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group, 0, 1000000).Id() << "'});\">"
+ "Trace 1 second</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group, 0, 10000000).Id() << "'});\">"
+ "Trace 10 seconds</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group, 0, 0, true).Id() << "'});\">"
+ "Trace 1000 tracks</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(group, 10000, 0, true).Id() << "'});\">"
+ "Trace 10000 tracks</a></li>"
+ "</ul>"
+ "</div>";
+ return ss.Str();
+ }
+
TString ProbeHtml(const TString& provider, const TString& probe)
- {
- TStringStream ss;
- ss << "<div class=\"dropdown\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << probe << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe).Id() << "'});\">"
- "Trace 1000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 10000).Id() << "'});\">"
- "Trace 10000 items</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 1000000).Id() << "'});\">"
- "Trace 1 second</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 10000000).Id() << "'});\">"
- "Trace 10 seconds</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 0, 0, true).Id() << "'});\">"
- "Trace 1000 tracks</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
- "{id:'" << TAdHocTraceConfig(provider, probe, 10000, 0, true).Id() << "'});\">"
- "Trace 10000 tracks</a></li>"
- "</ul>"
- "</div>";
- return ss.Str();
- }
+ {
+ TStringStream ss;
+ ss << "<div class=\"dropdown\">"
+ "<a href=\"#\" data-toggle=\"dropdown\">" << probe << "</a>"
+ "<ul class=\"dropdown-menu\">"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe).Id() << "'});\">"
+ "Trace 1000 items</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe, 10000).Id() << "'});\">"
+ "Trace 10000 items</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe, 0, 1000000).Id() << "'});\">"
+ "Trace 1 second</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe, 0, 10000000).Id() << "'});\">"
+ "Trace 10 seconds</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe, 0, 0, true).Id() << "'});\">"
+ "Trace 1000 tracks</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=new&ui=y&timeout=" << TimeoutSec << "',"
+ "{id:'" << TAdHocTraceConfig(provider, probe, 10000, 0, true).Id() << "'});\">"
+ "Trace 10000 tracks</a></li>"
+ "</ul>"
+ "</div>";
+ return ss.Str();
+ }
};
-void TDashboardRegistry::Register(const NLWTrace::TDashboard& dashboard) {
+void TDashboardRegistry::Register(const NLWTrace::TDashboard& dashboard) {
TGuard<TMutex> g(Mutex);
Dashboards[dashboard.GetName()] = dashboard;
}
-void TDashboardRegistry::Register(const TVector<NLWTrace::TDashboard>& dashboards) {
- for (const auto& dashboard : dashboards) {
- Register(dashboard);
- }
-}
-
+void TDashboardRegistry::Register(const TVector<NLWTrace::TDashboard>& dashboards) {
+ for (const auto& dashboard : dashboards) {
+ Register(dashboard);
+ }
+}
+
void TDashboardRegistry::Register(const TString& dashText) {
- NLWTrace::TDashboard dash;
+ NLWTrace::TDashboard dash;
if (!google::protobuf::TextFormat::ParseFromString(dashText, &dash)) {
ythrow yexception() << "Couldn't parse into dashboard";
}
Register(dash);
}
-bool TDashboardRegistry::Get(const TString& name, NLWTrace::TDashboard& dash) {
+bool TDashboardRegistry::Get(const TString& name, NLWTrace::TDashboard& dash) {
TGuard<TMutex> g(Mutex);
if (!Dashboards.contains(name)) {
return false;
@@ -1698,488 +1698,488 @@ void TDashboardRegistry::Output(TStringStream& ss) {
}
}
-class ILogSource {
-public:
- virtual ~ILogSource() {}
+class ILogSource {
+public:
+ virtual ~ILogSource() {}
virtual TString GetId() = 0;
- virtual TInstant GetStartTime() = 0;
- virtual TDuration GetTimeout(TInstant now) = 0;
- virtual ui64 GetEventsCount() = 0;
- virtual ui64 GetThreadsCount() = 0;
-};
-
-class TTraceLogSource : public ILogSource {
-private:
+ virtual TInstant GetStartTime() = 0;
+ virtual TDuration GetTimeout(TInstant now) = 0;
+ virtual ui64 GetEventsCount() = 0;
+ virtual ui64 GetThreadsCount() = 0;
+};
+
+class TTraceLogSource : public ILogSource {
+private:
TString Id;
- const TTraceCleaner& Cleaner;
- const NLWTrace::TSession* Trace;
-public:
- TTraceLogSource(const TString& id, const NLWTrace::TSession* trace, const TTraceCleaner& cleaner)
- : Id(id)
- , Cleaner(cleaner)
- , Trace(trace)
- {}
-
+ const TTraceCleaner& Cleaner;
+ const NLWTrace::TSession* Trace;
+public:
+ TTraceLogSource(const TString& id, const NLWTrace::TSession* trace, const TTraceCleaner& cleaner)
+ : Id(id)
+ , Cleaner(cleaner)
+ , Trace(trace)
+ {}
+
TString GetId() override
- {
- return Id;
- }
-
- TInstant GetStartTime() override
- {
- return Trace->GetStartTime();
- }
-
- TDuration GetTimeout(TInstant now) override
- {
- TInstant deadline = Cleaner.GetDeadline(Id);
- if (deadline < now) {
- return TDuration::Zero();
- } else if (deadline == TInstant::Max()) {
- return TDuration::Max();
- } else {
- return deadline - now;
- }
- }
-
- ui64 GetEventsCount() override
- {
- return Trace->GetEventsCount();
- }
-
- ui64 GetThreadsCount() override
- {
- return Trace->GetThreadsCount();
- }
-};
-
-class TSnapshotLogSource : public ILogSource {
-private:
+ {
+ return Id;
+ }
+
+ TInstant GetStartTime() override
+ {
+ return Trace->GetStartTime();
+ }
+
+ TDuration GetTimeout(TInstant now) override
+ {
+ TInstant deadline = Cleaner.GetDeadline(Id);
+ if (deadline < now) {
+ return TDuration::Zero();
+ } else if (deadline == TInstant::Max()) {
+ return TDuration::Max();
+ } else {
+ return deadline - now;
+ }
+ }
+
+ ui64 GetEventsCount() override
+ {
+ return Trace->GetEventsCount();
+ }
+
+ ui64 GetThreadsCount() override
+ {
+ return Trace->GetThreadsCount();
+ }
+};
+
+class TSnapshotLogSource : public ILogSource {
+private:
TString Sid;
- // Log should be used for read-only purpose, because it can be accessed from multiple threads
- // Atomic pointer is used to avoid thread-safety issues with snapshot deletion
- // (I hope protobuf const-implementation doesn't use any mutable non-thread-safe stuff inside)
- TAtomicSharedPtr<NLWTrace::TLogPb> Log;
-public:
- // Constructor should be called under SnapshotsMtx lock
+ // Log should be used for read-only purpose, because it can be accessed from multiple threads
+ // Atomic pointer is used to avoid thread-safety issues with snapshot deletion
+ // (I hope protobuf const-implementation doesn't use any mutable non-thread-safe stuff inside)
+ TAtomicSharedPtr<NLWTrace::TLogPb> Log;
+public:
+ // Constructor should be called under SnapshotsMtx lock
TSnapshotLogSource(const TString& sid, const TAtomicSharedPtr<NLWTrace::TLogPb>& log)
- : Sid(sid)
- , Log(log)
- {}
-
+ : Sid(sid)
+ , Log(log)
+ {}
+
TString GetId() override
- {
- return Sid + "~";
- }
-
- TInstant GetStartTime() override
- {
- return TInstant::MicroSeconds(Log->GetCrtTime());
- }
-
- TDuration GetTimeout(TInstant now) override
- {
- Y_UNUSED(now);
- return TDuration::Max();
- }
-
- ui64 GetEventsCount() override
- {
- return Log->GetEventsCount();
- }
-
- ui64 GetThreadsCount() override
- {
- return Log->ThreadLogsSize();
- }
-};
-
-class TLogSources {
-private:
- TTraceCleaner& Cleaner;
- TInstant Now;
+ {
+ return Sid + "~";
+ }
+
+ TInstant GetStartTime() override
+ {
+ return TInstant::MicroSeconds(Log->GetCrtTime());
+ }
+
+ TDuration GetTimeout(TInstant now) override
+ {
+ Y_UNUSED(now);
+ return TDuration::Max();
+ }
+
+ ui64 GetEventsCount() override
+ {
+ return Log->GetEventsCount();
+ }
+
+ ui64 GetThreadsCount() override
+ {
+ return Log->ThreadLogsSize();
+ }
+};
+
+class TLogSources {
+private:
+ TTraceCleaner& Cleaner;
+ TInstant Now;
using TLogSourcePtr = std::unique_ptr<ILogSource>;
TMap<TString, TLogSourcePtr> LogSources;
-public:
- explicit TLogSources(TTraceCleaner& cleaner, TInstant now = TInstant::Now())
- : Cleaner(cleaner)
- , Now(now)
- {}
-
+public:
+ explicit TLogSources(TTraceCleaner& cleaner, TInstant now = TInstant::Now())
+ : Cleaner(cleaner)
+ , Now(now)
+ {}
+
void Push(const TString& sid, const TAtomicSharedPtr<NLWTrace::TLogPb>& log)
- {
- TLogSourcePtr ls(new TSnapshotLogSource(sid, log));
+ {
+ TLogSourcePtr ls(new TSnapshotLogSource(sid, log));
LogSources.emplace(ls->GetId(), std::move(ls));
- }
-
- void Push(const TString& id, const NLWTrace::TSession* trace)
- {
- TLogSourcePtr ls(new TTraceLogSource(id, trace, Cleaner));
+ }
+
+ void Push(const TString& id, const NLWTrace::TSession* trace)
+ {
+ TLogSourcePtr ls(new TTraceLogSource(id, trace, Cleaner));
LogSources.emplace(ls->GetId(), std::move(ls));
- }
-
- template <class TFunc>
- void ForEach(TFunc& func)
- {
- for (auto& kv : LogSources) {
+ }
+
+ template <class TFunc>
+ void ForEach(TFunc& func)
+ {
+ for (auto& kv : LogSources) {
func.Push(kv.second.get());
- }
- }
-
- template <class TFunc>
- void ForEach(TFunc& func) const
- {
- for (const auto& kv : LogSources) {
+ }
+ }
+
+ template <class TFunc>
+ void ForEach(TFunc& func) const
+ {
+ for (const auto& kv : LogSources) {
func.Push(kv.second.get());
- }
- }
-};
-
+ }
+ }
+};
+
class TTracesHtmlPrinter {
private:
IOutputStream& Os;
- TInstant Now;
+ TInstant Now;
public:
explicit TTracesHtmlPrinter(IOutputStream& os)
: Os(os)
- , Now(TInstant::Now())
+ , Now(TInstant::Now())
{}
- void Push(ILogSource* src)
+ void Push(ILogSource* src)
{
TString id = src->GetId();
Os << "<tr>";
Os << "<td>";
try {
- Os << src->GetStartTime().ToStringUpToSeconds();
+ Os << src->GetStartTime().ToStringUpToSeconds();
} catch (...) {
Os << "error: " << CurrentExceptionMessage();
}
Os << "</td>"
- << "<td><div class=\"dropdown\">"
- "<a href=\"#\" data-toggle=\"dropdown\">" << TimeoutToString(src->GetTimeout(Now)) << "</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=60', {id:'" << id << "'});\">1 min</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=600', {id:'" << id << "'});\">10 min</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=3600', {id:'" << id << "'});\">1 hour</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=86400', {id:'" << id << "'});\">1 day</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=604800', {id:'" << id << "'});\">1 week</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y', {id:'" << id << "'});\">no timeout</a></li>"
- "</ul>"
- "</div></td>"
+ << "<td><div class=\"dropdown\">"
+ "<a href=\"#\" data-toggle=\"dropdown\">" << TimeoutToString(src->GetTimeout(Now)) << "</a>"
+ "<ul class=\"dropdown-menu\">"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=60', {id:'" << id << "'});\">1 min</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=600', {id:'" << id << "'});\">10 min</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=3600', {id:'" << id << "'});\">1 hour</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=86400', {id:'" << id << "'});\">1 day</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y&timeout=604800', {id:'" << id << "'});\">1 week</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=settimeout&ui=y', {id:'" << id << "'});\">no timeout</a></li>"
+ "</ul>"
+ "</div></td>"
<< "<td>" << EncodeHtmlPcdata(id) << "</td>"
- << "<td>" << src->GetEventsCount() << "</td>"
- << "<td>" << src->GetThreadsCount() << "</td>"
+ << "<td>" << src->GetEventsCount() << "</td>"
+ << "<td>" << src->GetThreadsCount() << "</td>"
<< "<td><a href=\"?mode=log&id=" << id << "\">Text</a></td>"
<< "<td><a href=\"?mode=log&format=json&id=" << id << "\">Json</a></td>"
<< "<td><a href=\"?mode=query&id=" << id << "\">Query</a></td>"
- << "<td><a href=\"?mode=analytics&id=" << id << "\">Analytics</a></td>"
- << "<td><div class=\"dropdown navbar-right\">" // navbar-right is hack to drop left
- "<a href=\"#\" data-toggle=\"dropdown\">Modify</a>"
- "<ul class=\"dropdown-menu\">"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=make_snapshot&ui=y', {id:'" << id << "'});\">Snapshot</a></li>"
- "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=delete&ui=y', {id:'" << id << "'});\">Delete</a></li>"
- "</ul>"
- "</div></td>"
+ << "<td><a href=\"?mode=analytics&id=" << id << "\">Analytics</a></td>"
+ << "<td><div class=\"dropdown navbar-right\">" // navbar-right is hack to drop left
+ "<a href=\"#\" data-toggle=\"dropdown\">Modify</a>"
+ "<ul class=\"dropdown-menu\">"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=make_snapshot&ui=y', {id:'" << id << "'});\">Snapshot</a></li>"
+ "<li><a href=\"#\" onClick=\"$.redirectPost('?mode=delete&ui=y', {id:'" << id << "'});\">Delete</a></li>"
+ "</ul>"
+ "</div></td>"
<< "</tr>\n";
}
-private:
+private:
static TString TimeoutToString(TDuration d)
- {
- TStringStream ss;
- if (d == TDuration::Zero()) {
- ss << "0";
- } else if (d == TDuration::Max()) {
- ss << "-";
- } else {
- ui64 us = d.GetValue();
- ui64 ms = us / 1000;
- ui64 sec = ms / 1000;
- ui64 min = sec / 60;
- ui64 hours = min / 60;
- ui64 days = hours / 24;
- ui64 weeks = days / 7;
- us -= ms * 1000;
- ms -= sec * 1000;
- sec -= min * 60;
- min -= hours * 60;
- hours -= days * 24;
- days -= weeks * 7;
- int terms = 0;
- if ((terms > 0 && terms < 2) || ( terms == 0 && weeks)) { ss << (ss.Str()? " ": "") << weeks << "w"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && days)) { ss << (ss.Str()? " ": "") << days << "d"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && hours)) { ss << (ss.Str()? " ": "") << hours << "h"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && min)) { ss << (ss.Str()? " ": "") << min << "m"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && sec)) { ss << (ss.Str()? " ": "") << sec << "s"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && ms)) { ss << (ss.Str()? " ": "") << ms << "ms"; terms++; }
- if ((terms > 0 && terms < 2) || ( terms == 0 && us)) { ss << (ss.Str()? " ": "") << us << "us"; terms++; }
- }
- return ss.Str();
- }
+ {
+ TStringStream ss;
+ if (d == TDuration::Zero()) {
+ ss << "0";
+ } else if (d == TDuration::Max()) {
+ ss << "-";
+ } else {
+ ui64 us = d.GetValue();
+ ui64 ms = us / 1000;
+ ui64 sec = ms / 1000;
+ ui64 min = sec / 60;
+ ui64 hours = min / 60;
+ ui64 days = hours / 24;
+ ui64 weeks = days / 7;
+ us -= ms * 1000;
+ ms -= sec * 1000;
+ sec -= min * 60;
+ min -= hours * 60;
+ hours -= days * 24;
+ days -= weeks * 7;
+ int terms = 0;
+ if ((terms > 0 && terms < 2) || ( terms == 0 && weeks)) { ss << (ss.Str()? " ": "") << weeks << "w"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && days)) { ss << (ss.Str()? " ": "") << days << "d"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && hours)) { ss << (ss.Str()? " ": "") << hours << "h"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && min)) { ss << (ss.Str()? " ": "") << min << "m"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && sec)) { ss << (ss.Str()? " ": "") << sec << "s"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && ms)) { ss << (ss.Str()? " ": "") << ms << "ms"; terms++; }
+ if ((terms > 0 && terms < 2) || ( terms == 0 && us)) { ss << (ss.Str()? " ": "") << us << "us"; terms++; }
+ }
+ return ss.Str();
+ }
};
-class TTracesLister {
-private:
- TVariants& Variants;
-public:
- TTracesLister(TVariants& variants)
- : Variants(variants)
- {}
- void Push(ILogSource* src)
- {
- Variants.emplace_back(src->GetId(), src->GetId());
- }
-};
-
-TVariants ListTraces(const TLogSources& srcs)
-{
- TVariants variants;
- TTracesLister lister(variants);
- srcs.ForEach(lister);
- return variants;
-}
-
-class TTimestampCutter {
-private:
+class TTracesLister {
+private:
+ TVariants& Variants;
+public:
+ TTracesLister(TVariants& variants)
+ : Variants(variants)
+ {}
+ void Push(ILogSource* src)
+ {
+ Variants.emplace_back(src->GetId(), src->GetId());
+ }
+};
+
+TVariants ListTraces(const TLogSources& srcs)
+{
+ TVariants variants;
+ TTracesLister lister(variants);
+ srcs.ForEach(lister);
+ return variants;
+}
+
+class TTimestampCutter {
+private:
THashMap<TThread::TId, std::pair<ui64, TInstant>> CutTsForThread; // tid -> time of first item
- mutable ui64 CutTsMax = 0;
- mutable TInstant CutInstantMax;
- bool Enabled;
- ui64 NowTs;
-public:
- explicit TTimestampCutter(bool enabled)
- : Enabled(enabled)
- , NowTs(GetCycleCount())
- {}
-
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- auto it = CutTsForThread.find(tid);
- if (it != CutTsForThread.end()) {
- ui64& ts = it->second.first;
- TInstant& inst = it->second.second;
- ts = Min(ts, item.TimestampCycles);
- inst = Min(inst, item.Timestamp);
- } else {
- CutTsForThread[tid] = std::make_pair(item.TimestampCycles, item.Timestamp);
- }
- }
-
- // Timestamp from which we are ensured that cyclic log for every thread is not truncated
- // NOTE: should NOT be called from Push(tid, item) functions
- ui64 StartTimestamp() const
- {
- if (CutTsMax == 0) {
- FindStartTime();
- }
- return CutTsMax;
- }
-
- ui64 NowTimestamp() const
- {
- return NowTs;
- }
-
- TInstant StartInstant() const
- {
- if (CutInstantMax == TInstant::Zero()) {
- FindStartTime();
- }
- return CutInstantMax;
- }
-
- // Returns true iff item should be skipped to avoid surprizes
- bool Skip(const NLWTrace::TLogItem& item) const
- {
- return Enabled && item.TimestampCycles < StartTimestamp();
- }
-
-private:
- void FindStartTime() const
- {
- for (auto& kv : CutTsForThread) {
- CutTsMax = Max(CutTsMax, kv.second.first);
- CutInstantMax = Max(CutInstantMax, kv.second.second);
- }
- }
-};
-
+ mutable ui64 CutTsMax = 0;
+ mutable TInstant CutInstantMax;
+ bool Enabled;
+ ui64 NowTs;
+public:
+ explicit TTimestampCutter(bool enabled)
+ : Enabled(enabled)
+ , NowTs(GetCycleCount())
+ {}
+
+ void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ auto it = CutTsForThread.find(tid);
+ if (it != CutTsForThread.end()) {
+ ui64& ts = it->second.first;
+ TInstant& inst = it->second.second;
+ ts = Min(ts, item.TimestampCycles);
+ inst = Min(inst, item.Timestamp);
+ } else {
+ CutTsForThread[tid] = std::make_pair(item.TimestampCycles, item.Timestamp);
+ }
+ }
+
+ // Timestamp from which we are ensured that cyclic log for every thread is not truncated
+ // NOTE: should NOT be called from Push(tid, item) functions
+ ui64 StartTimestamp() const
+ {
+ if (CutTsMax == 0) {
+ FindStartTime();
+ }
+ return CutTsMax;
+ }
+
+ ui64 NowTimestamp() const
+ {
+ return NowTs;
+ }
+
+ TInstant StartInstant() const
+ {
+ if (CutInstantMax == TInstant::Zero()) {
+ FindStartTime();
+ }
+ return CutInstantMax;
+ }
+
+ // Returns true iff item should be skipped to avoid surprizes
+ bool Skip(const NLWTrace::TLogItem& item) const
+ {
+ return Enabled && item.TimestampCycles < StartTimestamp();
+ }
+
+private:
+ void FindStartTime() const
+ {
+ for (auto& kv : CutTsForThread) {
+ CutTsMax = Max(CutTsMax, kv.second.first);
+ CutInstantMax = Max(CutInstantMax, kv.second.second);
+ }
+ }
+};
+
class TLogFilter {
private:
- struct TFilter {
+ struct TFilter {
TString ParamName;
TString ParamValue;
- bool Parsed;
-
- TLogQuery Query;
- NLWTrace::TLiteral Value;
-
+ bool Parsed;
+
+ TLogQuery Query;
+ NLWTrace::TLiteral Value;
+
explicit TFilter(const TString& text)
- {
- if (!text) { // Neither ParamName nor ParamValue is selected
- ParamName.clear();
- ParamValue.clear();
- Parsed = false;
- return;
- }
- size_t pos = text.find('=');
+ {
+ if (!text) { // Neither ParamName nor ParamValue is selected
+ ParamName.clear();
+ ParamValue.clear();
+ Parsed = false;
+ return;
+ }
+ size_t pos = text.find('=');
if (pos == TString::npos) { // Only ParamName has been selected
- ParamName = text;
- ParamValue.clear();
- Parsed = false;
- return;
- }
- // Both ParamName and ParamValue have been selected
- ParamValue = text.substr(pos + 1);
- ParamName = text.substr(0, pos);
- Parsed = true;
-
- Query = TLogQuery(ParamName);
- Value = NLWTrace::TLiteral(ParamValue);
- }
- };
+ ParamName = text;
+ ParamValue.clear();
+ Parsed = false;
+ return;
+ }
+ // Both ParamName and ParamValue have been selected
+ ParamValue = text.substr(pos + 1);
+ ParamName = text.substr(0, pos);
+ Parsed = true;
+
+ Query = TLogQuery(ParamName);
+ Value = NLWTrace::TLiteral(ParamValue);
+ }
+ };
TVector<TFilter> Filters;
THashSet<const NLWTrace::TSignature*> Signatures; // Just to list param names
- TVariants ParamNames;
+ TVariants ParamNames;
THashMap<TString, THashSet<TString>> FilteredParamValues; // paramName -> { paramValue }
public:
explicit TLogFilter(const TVector<TString>& filters)
{
for (const TString& subvalue : filters) {
- TFilter filter(subvalue);
- FilteredParamValues[filter.ParamName]; // just create empty set to gather values later
- if (filter.Parsed) {
- Filters.push_back(filter);
- }
+ TFilter filter(subvalue);
+ FilteredParamValues[filter.ParamName]; // just create empty set to gather values later
+ if (filter.Parsed) {
+ Filters.push_back(filter);
+ }
}
}
- virtual ~TLogFilter() {}
-
- template <class TLog>
- bool Filter(const TLog& log)
+ virtual ~TLogFilter() {}
+
+ template <class TLog>
+ bool Filter(const TLog& log)
{
- Gather(log);
- for (const TFilter& filter : Filters) {
- if (filter.Query.ExecuteQuery(log) != filter.Value) {
- return false;
+ Gather(log);
+ for (const TFilter& filter : Filters) {
+ if (filter.Query.ExecuteQuery(log) != filter.Value) {
+ return false;
}
}
- return true;
+ return true;
}
-
+
void FilterSelectors(TStringStream& ss, const TCgiParameters& e, const TString& fparam)
- {
- bool first = true;
- bool allParsed = true;
+ {
+ bool first = true;
+ bool allParsed = true;
for (const TString& subvalue : Subvalues(e, fparam)) {
- TFilter filter(subvalue);
- allParsed = allParsed && filter.Parsed;
- if (first) {
- SelectorTitle(ss, "where");
- }
- DropdownSelector<Erasable | CompositeValue, true>(
- ss, e, fparam, subvalue, first? "": ", ", ListParamNames(),
- filter.ParamName
- );
- if (filter.ParamName) {
- DropdownSelector<Link | CompositeValue, true>(
- ss, e, fparam, subvalue, "=", ListParamValues(filter.ParamName),
- filter.ParamValue? (filter.ParamName + "=" + filter.ParamValue): ""
- );
- }
- first = false;
- }
-
- if (!allParsed) {
- throw TPageGen<TSelectorsContainer>(ss.Str());
- } else {
- BtnHref<Button|ExtraSmall>(ss, first? "where": "+", MakeUrlAddSub(e, fparam, ""));
- }
- }
-
- const TVariants& ListParamNames()
- {
- if (ParamNames.empty()) {
+ TFilter filter(subvalue);
+ allParsed = allParsed && filter.Parsed;
+ if (first) {
+ SelectorTitle(ss, "where");
+ }
+ DropdownSelector<Erasable | CompositeValue, true>(
+ ss, e, fparam, subvalue, first? "": ", ", ListParamNames(),
+ filter.ParamName
+ );
+ if (filter.ParamName) {
+ DropdownSelector<Link | CompositeValue, true>(
+ ss, e, fparam, subvalue, "=", ListParamValues(filter.ParamName),
+ filter.ParamValue? (filter.ParamName + "=" + filter.ParamValue): ""
+ );
+ }
+ first = false;
+ }
+
+ if (!allParsed) {
+ throw TPageGen<TSelectorsContainer>(ss.Str());
+ } else {
+ BtnHref<Button|ExtraSmall>(ss, first? "where": "+", MakeUrlAddSub(e, fparam, ""));
+ }
+ }
+
+ const TVariants& ListParamNames()
+ {
+ if (ParamNames.empty()) {
THashSet<TString> paramNames;
- for (const NLWTrace::TSignature* sgn: Signatures) {
- for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
- paramNames.insert(sgn->ParamNames[pi]);
- }
- }
- for (auto& pn : paramNames) {
- ParamNames.emplace_back(pn, pn);
- }
- }
- return ParamNames;
- }
-
+ for (const NLWTrace::TSignature* sgn: Signatures) {
+ for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
+ paramNames.insert(sgn->ParamNames[pi]);
+ }
+ }
+ for (auto& pn : paramNames) {
+ ParamNames.emplace_back(pn, pn);
+ }
+ }
+ return ParamNames;
+ }
+
bool IsFiltered(const TString& paramName) const
- {
+ {
return FilteredParamValues.contains(paramName);
- }
-
-private:
- // Gather param names and values for selectors
- void Gather(const NLWTrace::TLogItem& item)
- {
- Signatures.insert(&item.Probe->Event.Signature);
- if (!FilteredParamValues.empty() && item.SavedParamsCount > 0) {
+ }
+
+private:
+ // Gather param names and values for selectors
+ void Gather(const NLWTrace::TLogItem& item)
+ {
+ Signatures.insert(&item.Probe->Event.Signature);
+ if (!FilteredParamValues.empty() && item.SavedParamsCount > 0) {
TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- auto iter = FilteredParamValues.find(item.Probe->Event.Signature.ParamNames[pi]);
- if (iter != FilteredParamValues.end()) {
- iter->second.insert(paramValues[pi]);
- }
- }
- }
- }
-
- void Gather(const NLWTrace::TTrackLog& tl)
- {
- for (const NLWTrace::TLogItem& item : tl.Items) {
- Gather(item);
- }
- }
-
+ item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
+ for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
+ auto iter = FilteredParamValues.find(item.Probe->Event.Signature.ParamNames[pi]);
+ if (iter != FilteredParamValues.end()) {
+ iter->second.insert(paramValues[pi]);
+ }
+ }
+ }
+ }
+
+ void Gather(const NLWTrace::TTrackLog& tl)
+ {
+ for (const NLWTrace::TLogItem& item : tl.Items) {
+ Gather(item);
+ }
+ }
+
TVariants ListParamValues(const TString& paramName) const
- {
- TVariants result;
- auto iter = FilteredParamValues.find(paramName);
- if (iter != FilteredParamValues.end()) {
+ {
+ TVariants result;
+ auto iter = FilteredParamValues.find(paramName);
+ if (iter != FilteredParamValues.end()) {
for (const TString& paramValue : iter->second) {
- result.emplace_back(paramName + "=" + paramValue, paramValue);
- }
- }
- Sort(result.begin(), result.end());
- return result;
- }
+ result.emplace_back(paramName + "=" + paramValue, paramValue);
+ }
+ }
+ Sort(result.begin(), result.end());
+ return result;
+ }
};
-static void EscapeJSONString(IOutputStream& os, const TString& s)
-{
- for (TString::const_iterator i = s.begin(), e = s.end(); i != e; ++i) {
- char c = *i;
- if (c < ' ') {
- os << Sprintf("\\u%04x", int(c));
- } else if (c == '"') {
- os << "\\\"";
- } else if (c == '\\') {
- os << "\\\\";
- } else {
- os << c;
- }
- }
-}
-
-static TString EscapeJSONString(const TString& s)
-{
- TStringStream ss;
- EscapeJSONString(ss, s);
- return ss.Str();
-}
-
+static void EscapeJSONString(IOutputStream& os, const TString& s)
+{
+ for (TString::const_iterator i = s.begin(), e = s.end(); i != e; ++i) {
+ char c = *i;
+ if (c < ' ') {
+ os << Sprintf("\\u%04x", int(c));
+ } else if (c == '"') {
+ os << "\\\"";
+ } else if (c == '\\') {
+ os << "\\\\";
+ } else {
+ os << c;
+ }
+ }
+}
+
+static TString EscapeJSONString(const TString& s)
+{
+ TStringStream ss;
+ EscapeJSONString(ss, s);
+ return ss.Str();
+}
+
class TLogJsonPrinter {
private:
IOutputStream& Os;
@@ -2199,15 +2199,15 @@ public:
;
}
- void OutputFooter(const NLWTrace::TSession* trace)
+ void OutputFooter(const NLWTrace::TSession* trace)
{
Os << "\n\t\t]"
"\n\t, \"threads\": ["
;
- trace->ReadThreads(*this);
+ trace->ReadThreads(*this);
Os << "]"
- "\n\t, \"events_count\": " << trace->GetEventsCount() <<
- "\n\t, \"threads_count\": " << trace->GetThreadsCount() <<
+ "\n\t, \"events_count\": " << trace->GetEventsCount() <<
+ "\n\t, \"threads_count\": " << trace->GetThreadsCount() <<
"\n\t, \"timestamp\": " << Now().GetValue() <<
"\n}"
;
@@ -2219,7 +2219,7 @@ public:
FirstThread = false;
}
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
+ void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
{
Os << "\n\t\t" << (FirstItem? "": ", ");
FirstItem = false;
@@ -2246,31 +2246,31 @@ public:
class TLogTextPrinter : public TLogFilter {
private:
- TMultiMap<NLWTrace::TTypedParam, std::pair<TThread::TId, NLWTrace::TLogItem> > Items;
- TMultiMap<NLWTrace::TTypedParam, NLWTrace::TTrackLog> Depot;
- THashMap<NLWTrace::TProbe*, size_t> ProbeId;
- TVector<NLWTrace::TProbe*> Probes;
- TTimestampCutter CutTs;
- TLogQuery Order;
- bool ReverseOrder = false;
- ui64 Head = 0;
- ui64 Tail = 0;
- bool ShowTs = false;
+ TMultiMap<NLWTrace::TTypedParam, std::pair<TThread::TId, NLWTrace::TLogItem> > Items;
+ TMultiMap<NLWTrace::TTypedParam, NLWTrace::TTrackLog> Depot;
+ THashMap<NLWTrace::TProbe*, size_t> ProbeId;
+ TVector<NLWTrace::TProbe*> Probes;
+ TTimestampCutter CutTs;
+ TLogQuery Order;
+ bool ReverseOrder = false;
+ ui64 Head = 0;
+ ui64 Tail = 0;
+ bool ShowTs = false;
public:
- TLogTextPrinter(const TVector<TString>& filters, ui64 head, ui64 tail, const TString& order, bool reverseOrder, bool cutTs, bool showTs)
- : TLogFilter(filters)
- , CutTs(cutTs)
- , Order(order)
- , ReverseOrder(reverseOrder)
- , Head(head)
- , Tail(tail)
- , ShowTs(showTs)
- {}
-
+ TLogTextPrinter(const TVector<TString>& filters, ui64 head, ui64 tail, const TString& order, bool reverseOrder, bool cutTs, bool showTs)
+ : TLogFilter(filters)
+ , CutTs(cutTs)
+ , Order(order)
+ , ReverseOrder(reverseOrder)
+ , Head(head)
+ , Tail(tail)
+ , ShowTs(showTs)
+ {}
+
TLogTextPrinter(const TCgiParameters& e)
: TLogTextPrinter(
- Subvalues(e, "f"),
- e.Has("head")? FromString<ui64>(e.Get("head")): 0,
+ Subvalues(e, "f"),
+ e.Has("head")? FromString<ui64>(e.Get("head")): 0,
e.Has("tail")? FromString<ui64>(e.Get("tail")): 0,
e.Get("s"),
e.Get("reverse") == "y",
@@ -2278,1630 +2278,1630 @@ public:
e.Get("showts") == "y")
{}
- enum EFormat {
- Text,
- Json
- };
-
+ enum EFormat {
+ Text,
+ Json
+ };
+
void Output(IOutputStream& os) const
{
- OutputItems<Text>(os);
- OutputDepot<Text>(os);
- }
-
- void OutputJson(IOutputStream& os) const
- {
- os << "{\"depot\":[\n";
- OutputItems<Json>(os);
- OutputDepot<Json>(os);
- os << "],\"probes\":[";
- bool first = true;
- for (const NLWTrace::TProbe* probe : Probes) {
- os << (first? "": ",") << "{\"provider\":\"" << probe->Event.GetProvider()
- << "\",\"name\":\"" << probe->Event.Name << "\"}";
- first = false;
- }
- os << "]}";
- }
-
- NLWTrace::TTypedParam GetKey(const NLWTrace::TLogItem& item)
- {
- return Order? Order.ExecuteQuery(item): NLWTrace::TTypedParam(item.GetTimestampCycles());
- }
-
- NLWTrace::TTypedParam GetKey(const NLWTrace::TTrackLog& tl)
- {
- return Order? Order.ExecuteQuery(tl): NLWTrace::TTypedParam(tl.GetTimestampCycles());
- }
-
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
+ OutputItems<Text>(os);
+ OutputDepot<Text>(os);
+ }
+
+ void OutputJson(IOutputStream& os) const
+ {
+ os << "{\"depot\":[\n";
+ OutputItems<Json>(os);
+ OutputDepot<Json>(os);
+ os << "],\"probes\":[";
+ bool first = true;
+ for (const NLWTrace::TProbe* probe : Probes) {
+ os << (first? "": ",") << "{\"provider\":\"" << probe->Event.GetProvider()
+ << "\",\"name\":\"" << probe->Event.Name << "\"}";
+ first = false;
+ }
+ os << "]}";
+ }
+
+ NLWTrace::TTypedParam GetKey(const NLWTrace::TLogItem& item)
+ {
+ return Order? Order.ExecuteQuery(item): NLWTrace::TTypedParam(item.GetTimestampCycles());
+ }
+
+ NLWTrace::TTypedParam GetKey(const NLWTrace::TTrackLog& tl)
+ {
+ return Order? Order.ExecuteQuery(tl): NLWTrace::TTypedParam(tl.GetTimestampCycles());
+ }
+
+ void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ CutTs.Push(tid, item);
if (Filter(item)) {
- AddId(item);
- Items.emplace(GetKey(item), std::make_pair(tid, item));
- }
- }
-
- void Push(TThread::TId tid, const NLWTrace::TTrackLog& tl)
- {
- Y_UNUSED(tid);
- if (Filter(tl)) {
- AddId(tl);
- Depot.emplace(GetKey(tl), tl);
- }
- }
-
+ AddId(item);
+ Items.emplace(GetKey(item), std::make_pair(tid, item));
+ }
+ }
+
+ void Push(TThread::TId tid, const NLWTrace::TTrackLog& tl)
+ {
+ Y_UNUSED(tid);
+ if (Filter(tl)) {
+ AddId(tl);
+ Depot.emplace(GetKey(tl), tl);
+ }
+ }
+
private:
- void AddId(const NLWTrace::TLogItem& item)
- {
- if (ProbeId.find(item.Probe) == ProbeId.end()) {
- size_t id = Probes.size();
- ProbeId[item.Probe] = id;
- Probes.emplace_back(item.Probe);
- }
- }
-
- void AddId(const NLWTrace::TTrackLog& tl)
- {
- for (const auto& item : tl.Items) {
- AddId(item);
- }
- }
-
- bool HeadTailFilter(ui64 idx, ui64 size) const
- {
- bool headOk = idx < Head;
- bool tailOk = size < Tail + idx + 1ull;
- if (Head && Tail) {
- return headOk || tailOk;
- } else if (Head) {
- return headOk;
- } else if (Tail) {
- return tailOk;
- } else {
- return true;
- }
- }
-
- template <EFormat Format>
+ void AddId(const NLWTrace::TLogItem& item)
+ {
+ if (ProbeId.find(item.Probe) == ProbeId.end()) {
+ size_t id = Probes.size();
+ ProbeId[item.Probe] = id;
+ Probes.emplace_back(item.Probe);
+ }
+ }
+
+ void AddId(const NLWTrace::TTrackLog& tl)
+ {
+ for (const auto& item : tl.Items) {
+ AddId(item);
+ }
+ }
+
+ bool HeadTailFilter(ui64 idx, ui64 size) const
+ {
+ bool headOk = idx < Head;
+ bool tailOk = size < Tail + idx + 1ull;
+ if (Head && Tail) {
+ return headOk || tailOk;
+ } else if (Head) {
+ return headOk;
+ } else if (Tail) {
+ return tailOk;
+ } else {
+ return true;
+ }
+ }
+
+ template <EFormat Format>
void OutputItems(IOutputStream& os) const
- {
- ui64 idx = 0;
- ui64 size = Items.size();
- ui64 startTs = ShowTs? CutTs.StartTimestamp(): 0;
- ui64 prevTs = 0;
- bool first = true;
- if (!ReverseOrder) {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
- prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
- }
- }
- } else {
- for (auto i = Items.rbegin(), e = Items.rend(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
- prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
- }
- }
- }
- }
-
- template <EFormat Format>
+ {
+ ui64 idx = 0;
+ ui64 size = Items.size();
+ ui64 startTs = ShowTs? CutTs.StartTimestamp(): 0;
+ ui64 prevTs = 0;
+ bool first = true;
+ if (!ReverseOrder) {
+ for (auto i = Items.begin(), e = Items.end(); i != e; ++i, idx++) {
+ if (HeadTailFilter(idx, size)) {
+ OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
+ prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
+ }
+ }
+ } else {
+ for (auto i = Items.rbegin(), e = Items.rend(); i != e; ++i, idx++) {
+ if (HeadTailFilter(idx, size)) {
+ OutputItem<Format, true>(os, i->second.first, i->second.second, startTs, prevTs, first);
+ prevTs = startTs? i->second.second.GetTimestampCycles(): 0;
+ }
+ }
+ }
+ }
+
+ template <EFormat Format>
void OutputDepot(IOutputStream& os) const
- {
- ui64 idx = 0;
- ui64 size = Depot.size();
- bool first = true;
- if (!ReverseOrder) {
- for (auto i = Depot.begin(), e = Depot.end(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputTrackLog<Format>(os, i->second, first);
- }
- }
- } else {
- for (auto i = Depot.rbegin(), e = Depot.rend(); i != e; ++i, idx++) {
- if (HeadTailFilter(idx, size)) {
- OutputTrackLog<Format>(os, i->second, first);
- }
- }
- }
- }
-
- template <EFormat Format, bool AsTrack = false>
- void OutputItem(IOutputStream& os, TThread::TId tid, const NLWTrace::TLogItem& item, ui64 startTs, ui64 prevTs, bool& first) const
- {
- if (CutTs.Skip(item)) {
- return;
- }
- if constexpr (Format == Text) {
- if (startTs) {
- if (!prevTs) {
- prevTs = item.GetTimestampCycles();
- }
- os << Sprintf("%10.3lf %+10.3lf ms ",
- NHPTimer::GetSeconds(item.GetTimestampCycles() - startTs) * 1000.0,
- NHPTimer::GetSeconds(item.GetTimestampCycles() - prevTs) * 1000.0);
- }
- if (tid) {
- os << "<" << tid << "> ";
- }
- if (item.Timestamp != TInstant::Zero()) {
- os << "[" << item.Timestamp << "] ";
- } else {
- os << "[" << item.TimestampCycles << "] ";
- }
- os << GetProbeName(item.Probe) << "(";
- if (item.SavedParamsCount > 0) {
- TString ParamValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
- bool first = true;
- for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
- os << (first? "": ", ") << item.Probe->Event.Signature.ParamNames[i] << "='" << EscapeC(ParamValues[i]) << "'";
- }
- }
- os << ")\n";
- } else if constexpr (Format == Json) {
- if (auto probeId = ProbeId.find(item.Probe); probeId != ProbeId.end()) {
- os << (first? "": ",") << (AsTrack? "[":"") << "[\"" << tid << "\",\"";
- if (item.Timestamp != TInstant::Zero()) {
- os << item.Timestamp.MicroSeconds();
- } else {
- os << Sprintf("%.3lf", NHPTimer::GetSeconds(item.TimestampCycles) * 1e9);
- }
- os << "\"," << probeId->second << ",{";
- if (item.SavedParamsCount > 0) {
- TString ParamValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
- bool first = true;
- for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
- os << (first? "": ",") << "\"" << item.Probe->Event.Signature.ParamNames[i] << "\":\"";
- EscapeJSONString(os, ParamValues[i]);
- os << "\"";
- }
- }
- os << "}]" << (AsTrack? "]":"");
- }
- }
- first = false;
- }
-
- template <EFormat Format>
- void OutputTrackLog(IOutputStream& os, const NLWTrace::TTrackLog& tl, bool& first) const
- {
- if constexpr (Format == Json) {
- os << (first? "": ",") << "[";
- }
- first = false;
- ui64 prevTs = tl.GetTimestampCycles();
- bool firstItem = true;
- for (const NLWTrace::TTrackLog::TItem& item: tl.Items) {
- OutputItem<Format>(os, item.ThreadId, item, tl.GetTimestampCycles(), prevTs, firstItem);
- prevTs = item.GetTimestampCycles();
- }
- if constexpr (Format == Json) {
- os << "]";
- }
- os << "\n";
- }
+ {
+ ui64 idx = 0;
+ ui64 size = Depot.size();
+ bool first = true;
+ if (!ReverseOrder) {
+ for (auto i = Depot.begin(), e = Depot.end(); i != e; ++i, idx++) {
+ if (HeadTailFilter(idx, size)) {
+ OutputTrackLog<Format>(os, i->second, first);
+ }
+ }
+ } else {
+ for (auto i = Depot.rbegin(), e = Depot.rend(); i != e; ++i, idx++) {
+ if (HeadTailFilter(idx, size)) {
+ OutputTrackLog<Format>(os, i->second, first);
+ }
+ }
+ }
+ }
+
+ template <EFormat Format, bool AsTrack = false>
+ void OutputItem(IOutputStream& os, TThread::TId tid, const NLWTrace::TLogItem& item, ui64 startTs, ui64 prevTs, bool& first) const
+ {
+ if (CutTs.Skip(item)) {
+ return;
+ }
+ if constexpr (Format == Text) {
+ if (startTs) {
+ if (!prevTs) {
+ prevTs = item.GetTimestampCycles();
+ }
+ os << Sprintf("%10.3lf %+10.3lf ms ",
+ NHPTimer::GetSeconds(item.GetTimestampCycles() - startTs) * 1000.0,
+ NHPTimer::GetSeconds(item.GetTimestampCycles() - prevTs) * 1000.0);
+ }
+ if (tid) {
+ os << "<" << tid << "> ";
+ }
+ if (item.Timestamp != TInstant::Zero()) {
+ os << "[" << item.Timestamp << "] ";
+ } else {
+ os << "[" << item.TimestampCycles << "] ";
+ }
+ os << GetProbeName(item.Probe) << "(";
+ if (item.SavedParamsCount > 0) {
+ TString ParamValues[LWTRACE_MAX_PARAMS];
+ item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
+ bool first = true;
+ for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
+ os << (first? "": ", ") << item.Probe->Event.Signature.ParamNames[i] << "='" << EscapeC(ParamValues[i]) << "'";
+ }
+ }
+ os << ")\n";
+ } else if constexpr (Format == Json) {
+ if (auto probeId = ProbeId.find(item.Probe); probeId != ProbeId.end()) {
+ os << (first? "": ",") << (AsTrack? "[":"") << "[\"" << tid << "\",\"";
+ if (item.Timestamp != TInstant::Zero()) {
+ os << item.Timestamp.MicroSeconds();
+ } else {
+ os << Sprintf("%.3lf", NHPTimer::GetSeconds(item.TimestampCycles) * 1e9);
+ }
+ os << "\"," << probeId->second << ",{";
+ if (item.SavedParamsCount > 0) {
+ TString ParamValues[LWTRACE_MAX_PARAMS];
+ item.Probe->Event.Signature.SerializeParams(item.Params, ParamValues);
+ bool first = true;
+ for (size_t i = 0; i < item.SavedParamsCount; i++, first = false) {
+ os << (first? "": ",") << "\"" << item.Probe->Event.Signature.ParamNames[i] << "\":\"";
+ EscapeJSONString(os, ParamValues[i]);
+ os << "\"";
+ }
+ }
+ os << "}]" << (AsTrack? "]":"");
+ }
+ }
+ first = false;
+ }
+
+ template <EFormat Format>
+ void OutputTrackLog(IOutputStream& os, const NLWTrace::TTrackLog& tl, bool& first) const
+ {
+ if constexpr (Format == Json) {
+ os << (first? "": ",") << "[";
+ }
+ first = false;
+ ui64 prevTs = tl.GetTimestampCycles();
+ bool firstItem = true;
+ for (const NLWTrace::TTrackLog::TItem& item: tl.Items) {
+ OutputItem<Format>(os, item.ThreadId, item, tl.GetTimestampCycles(), prevTs, firstItem);
+ prevTs = item.GetTimestampCycles();
+ }
+ if constexpr (Format == Json) {
+ os << "]";
+ }
+ os << "\n";
+ }
};
-class TLogAnalyzer: public TLogFilter {
-private:
+class TLogAnalyzer: public TLogFilter {
+private:
TMultiMap<ui64, std::pair<TThread::TId, NLWTrace::TLogItem>> Items;
TVector<NLWTrace::TTrackLog> Depot;
THashMap<TString, TTrackLogRefs> Groups;
- NAnalytics::TTable Table;
- bool TableCreated = false;
+ NAnalytics::TTable Table;
+ bool TableCreated = false;
TVector<TString> GroupBy;
- TTimestampCutter CutTs;
-public:
+ TTimestampCutter CutTs;
+public:
TLogAnalyzer(const TVector<TString>& filters, const TVector<TString>& groupBy, bool cutTs)
- : TLogFilter(filters)
- , CutTs(cutTs)
- {
+ : TLogFilter(filters)
+ , CutTs(cutTs)
+ {
for (const TString& groupParam : groupBy) {
- GroupBy.push_back(groupParam);
- }
- }
-
- const NAnalytics::TTable& GetTable()
- {
- if (!TableCreated) {
- TableCreated = true;
- if (GroupBy.empty()) {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
- ParseItems(i->second.first, i->second.second);
- }
- ParseDepot();
- } else {
- for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
- Map(i->second.first, i->second.second);
- }
- Reduce();
- }
- }
- return Table;
- }
-
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
- if (Filter(item)) {
- Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
- }
- }
-
- void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
- {
- if (Filter(tl)) {
- Depot.emplace_back(tl);
- }
- }
-private:
- void FillRow(NAnalytics::TRow& row, const NLWTrace::TLogItem& item)
- {
- if (item.SavedParamsCount > 0) {
+ GroupBy.push_back(groupParam);
+ }
+ }
+
+ const NAnalytics::TTable& GetTable()
+ {
+ if (!TableCreated) {
+ TableCreated = true;
+ if (GroupBy.empty()) {
+ for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
+ ParseItems(i->second.first, i->second.second);
+ }
+ ParseDepot();
+ } else {
+ for (auto i = Items.begin(), e = Items.end(); i != e; ++i) {
+ Map(i->second.first, i->second.second);
+ }
+ Reduce();
+ }
+ }
+ return Table;
+ }
+
+ void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ CutTs.Push(tid, item);
+ if (Filter(item)) {
+ Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
+ }
+ }
+
+ void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
+ {
+ if (Filter(tl)) {
+ Depot.emplace_back(tl);
+ }
+ }
+private:
+ void FillRow(NAnalytics::TRow& row, const NLWTrace::TLogItem& item)
+ {
+ if (item.SavedParamsCount > 0) {
TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t i = 0; i < item.SavedParamsCount; i++) {
+ item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
+ for (size_t i = 0; i < item.SavedParamsCount; i++) {
double value = FromString<double>(paramValues[i].data(), paramValues[i].size(), NAN);
// If value cannot be cast to double or is inf/nan -- assume it's a string
- if (isfinite(value)) {
- row[item.Probe->Event.Signature.ParamNames[i]] = value;
+ if (isfinite(value)) {
+ row[item.Probe->Event.Signature.ParamNames[i]] = value;
} else {
row[item.Probe->Event.Signature.ParamNames[i]] = paramValues[i];
- }
- }
- }
- }
-
+ }
+ }
+ }
+ }
+
TString GetParam(const NLWTrace::TLogItem& item, TString* paramValues, const TString& paramName)
- {
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
- return paramValues[pi];
- }
- }
+ {
+ for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
+ if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
+ return paramValues[pi];
+ }
+ }
return TString();
- }
-
+ }
+
TString GetGroup(const NLWTrace::TLogItem& item, TString* paramValues)
- {
- TStringStream ss;
- bool first = true;
+ {
+ TStringStream ss;
+ bool first = true;
for (const TString& groupParam : GroupBy) {
- ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
- first = false;
- }
- return ss.Str();
- }
-
- void ParseItems(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- if (CutTs.Skip(item)) {
- return;
- }
+ ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
+ first = false;
+ }
+ return ss.Str();
+ }
+
+ void ParseItems(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ if (CutTs.Skip(item)) {
+ return;
+ }
Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- row["_thread"] = tid;
- if (item.Timestamp != TInstant::Zero()) {
- row["_wallTime"] = item.Timestamp.SecondsFloat();
- row["_wallRTime"] = item.Timestamp.SecondsFloat() - CutTs.StartInstant().SecondsFloat();
- }
- row["_cycles"] = item.TimestampCycles;
- row["_thrTime"] = CyclesToDuration((ui64)item.TimestampCycles).SecondsFloat();
- row["_thrRTime"] = double(i64(item.TimestampCycles) - i64(CutTs.StartTimestamp())) / NHPTimer::GetCyclesPerSecond();
- row["_thrNTime"] = double(i64(item.TimestampCycles) - i64(CutTs.NowTimestamp())) / NHPTimer::GetCyclesPerSecond();
- row.Name = GetProbeName(item.Probe);
- FillRow(row, item);
- }
-
- void Map(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
+ NAnalytics::TRow& row = Table.back();
+ row["_thread"] = tid;
+ if (item.Timestamp != TInstant::Zero()) {
+ row["_wallTime"] = item.Timestamp.SecondsFloat();
+ row["_wallRTime"] = item.Timestamp.SecondsFloat() - CutTs.StartInstant().SecondsFloat();
+ }
+ row["_cycles"] = item.TimestampCycles;
+ row["_thrTime"] = CyclesToDuration((ui64)item.TimestampCycles).SecondsFloat();
+ row["_thrRTime"] = double(i64(item.TimestampCycles) - i64(CutTs.StartTimestamp())) / NHPTimer::GetCyclesPerSecond();
+ row["_thrNTime"] = double(i64(item.TimestampCycles) - i64(CutTs.NowTimestamp())) / NHPTimer::GetCyclesPerSecond();
+ row.Name = GetProbeName(item.Probe);
+ FillRow(row, item);
+ }
+
+ void Map(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- TTrackLogRefs& tl = Groups[GetGroup(item, paramValues)];
- tl.Items.emplace_back(tid, item);
- }
- }
-
- void Reduce()
- {
- for (auto& v : Groups) {
+ item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
+ TTrackLogRefs& tl = Groups[GetGroup(item, paramValues)];
+ tl.Items.emplace_back(tid, item);
+ }
+ }
+
+ void Reduce()
+ {
+ for (auto& v : Groups) {
const TString& group = v.first;
- const TTrackLogRefs& tl = v.second;
+ const TTrackLogRefs& tl = v.second;
Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- row.Name = group;
- for (const NLWTrace::TLogItem& item : tl.Items) {
- FillRow(row, item);
- }
- }
- }
-
- void ParseDepot()
- {
- for (NLWTrace::TTrackLog& tl : Depot) {
- Table.emplace_back();
- NAnalytics::TRow& row = Table.back();
- for (const NLWTrace::TLogItem& item : tl.Items) {
- FillRow(row, item);
- }
- }
- }
-};
-
-struct TSampleOpts {
- bool ShowProvider = false;
- size_t SizeLimit = 50;
-};
-
-enum ENodeType {
- NT_ROOT,
- NT_PROBE,
- NT_PARAM
-};
-
-class TPatternTree;
-struct TPatternNode;
-
-struct TTrack : public TTrackLogRefs {
+ NAnalytics::TRow& row = Table.back();
+ row.Name = group;
+ for (const NLWTrace::TLogItem& item : tl.Items) {
+ FillRow(row, item);
+ }
+ }
+ }
+
+ void ParseDepot()
+ {
+ for (NLWTrace::TTrackLog& tl : Depot) {
+ Table.emplace_back();
+ NAnalytics::TRow& row = Table.back();
+ for (const NLWTrace::TLogItem& item : tl.Items) {
+ FillRow(row, item);
+ }
+ }
+ }
+};
+
+struct TSampleOpts {
+ bool ShowProvider = false;
+ size_t SizeLimit = 50;
+};
+
+enum ENodeType {
+ NT_ROOT,
+ NT_PROBE,
+ NT_PARAM
+};
+
+class TPatternTree;
+struct TPatternNode;
+
+struct TTrack : public TTrackLogRefs {
TString TrackId;
- TPatternNode* LastNode = nullptr;
-};
-
-using TTrackTr = TLogTraits<TTrackLogRefs>;
-using TTrackIter = TTrackTr::const_iterator;
-
-// Visitor for tree traversing
-class IVisitor {
-public:
- virtual ~IVisitor() {}
- virtual void Visit(TPatternNode* node) = 0;
-};
-
-// Per-node classifier
-class TClassifier {
-public:
- explicit TClassifier(TPatternNode* node, ENodeType childType, bool keepHead = false)
- : Node(node)
- , KeepHead(keepHead)
- , ChildType(childType)
- {}
- virtual ~TClassifier() {}
- virtual TPatternNode* Classify(TTrackIter cur, const TTrack& track) = 0;
- virtual void Accept(IVisitor* visitor) = 0;
- virtual bool IsLeaf() = 0;
- ENodeType GetChildType() const { return ChildType; }
-public:
- TPatternNode* Node;
- const bool KeepHead;
- ENodeType ChildType;
-};
-
-// Track classification tree node
-struct TPatternNode {
+ TPatternNode* LastNode = nullptr;
+};
+
+using TTrackTr = TLogTraits<TTrackLogRefs>;
+using TTrackIter = TTrackTr::const_iterator;
+
+// Visitor for tree traversing
+class IVisitor {
+public:
+ virtual ~IVisitor() {}
+ virtual void Visit(TPatternNode* node) = 0;
+};
+
+// Per-node classifier
+class TClassifier {
+public:
+ explicit TClassifier(TPatternNode* node, ENodeType childType, bool keepHead = false)
+ : Node(node)
+ , KeepHead(keepHead)
+ , ChildType(childType)
+ {}
+ virtual ~TClassifier() {}
+ virtual TPatternNode* Classify(TTrackIter cur, const TTrack& track) = 0;
+ virtual void Accept(IVisitor* visitor) = 0;
+ virtual bool IsLeaf() = 0;
+ ENodeType GetChildType() const { return ChildType; }
+public:
+ TPatternNode* Node;
+ const bool KeepHead;
+ ENodeType ChildType;
+};
+
+// Track classification tree node
+struct TPatternNode {
TString Name;
- TPatternNode* Parent = nullptr;
- THolder<TClassifier> Classifier;
- struct TDesc {
- ENodeType Type = NT_ROOT;
- // NT_PROBE
- const NLWTrace::TProbe* Probe = nullptr;
- // NT_PARAM
- size_t Rollbacks = 0;
+ TPatternNode* Parent = nullptr;
+ THolder<TClassifier> Classifier;
+ struct TDesc {
+ ENodeType Type = NT_ROOT;
+ // NT_PROBE
+ const NLWTrace::TProbe* Probe = nullptr;
+ // NT_PARAM
+ size_t Rollbacks = 0;
TString ParamName;
TString ParamValue;
- } Desc;
-
- ui64 TrackCount = 0;
- struct TTrackEntry {
- TTrack* Track;
- ui64 ResTotal;
- ui64 ResLast;
-
- TTrackEntry(TTrack* track, ui64 resTotal, ui64 resLast)
- : Track(track)
- , ResTotal(resTotal)
- , ResLast(resLast)
- {}
- };
-
+ } Desc;
+
+ ui64 TrackCount = 0;
+ struct TTrackEntry {
+ TTrack* Track;
+ ui64 ResTotal;
+ ui64 ResLast;
+
+ TTrackEntry(TTrack* track, ui64 resTotal, ui64 resLast)
+ : Track(track)
+ , ResTotal(resTotal)
+ , ResLast(resLast)
+ {}
+ };
+
TVector<TTrackEntry> Tracks;
-
- ui64 ResTotalSum = 0;
- ui64 ResTotalMax = 0;
+
+ ui64 ResTotalSum = 0;
+ ui64 ResTotalMax = 0;
TVector<ui64> ResTotalAll;
-
- ui64 ResLastSum = 0;
- ui64 ResLastMax = 0;
+
+ ui64 ResLastSum = 0;
+ ui64 ResLastMax = 0;
TVector<ui64> ResLastAll;
-
+
TVector<ui64> TimelineSum;
- NAnalytics::TTable Slices;
-
+ NAnalytics::TTable Slices;
+
TString GetPath() const
- {
- if (Parent) {
- return Parent->GetPath() + Name;
- }
- return "/";
- }
-
- NAnalytics::TTable GetTable() const
- {
- using namespace NAnalytics;
- NAnalytics::TTable ret;
- for (ui64 x : ResTotalAll) {
+ {
+ if (Parent) {
+ return Parent->GetPath() + Name;
+ }
+ return "/";
+ }
+
+ NAnalytics::TTable GetTable() const
+ {
+ using namespace NAnalytics;
+ NAnalytics::TTable ret;
+ for (ui64 x : ResTotalAll) {
ret.emplace_back();
- TRow& row = ret.back();
- row["resTotal"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
- }
- for (ui64 x : ResLastAll) {
+ TRow& row = ret.back();
+ row["resTotal"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
+ }
+ for (ui64 x : ResLastAll) {
ret.emplace_back();
- TRow& row = ret.back();
- row["resLast"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
- }
- return ret;
- }
-
+ TRow& row = ret.back();
+ row["resLast"] = double(x) * 1000.0 / NHPTimer::GetClockRate();
+ }
+ return ret;
+ }
+
template <typename TReader>
void OutputSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader) const
- {
- bool filterTotal = false;
- if (bn == "resTotal") {
- filterTotal = true;
- } else {
+ {
+ bool filterTotal = false;
+ if (bn == "resTotal") {
+ filterTotal = true;
+ } else {
WWW_CHECK(bn == "resLast", "wrong sample filter param: %s", bn.data());
- }
-
- size_t spaceLeft = opts.SizeLimit;
- for (const TTrackEntry& entry : Tracks) {
- const TTrack* track = entry.Track;
- // Filter out tracks that are not in sample
- if (filterTotal) {
- double resTotalMs = double(entry.ResTotal) * 1000.0 / NHPTimer::GetClockRate();
- if (resTotalMs < b1 || resTotalMs > b2) {
- continue;
- }
- } else {
- double resLastMs = double(entry.ResLast) * 1000.0 / NHPTimer::GetClockRate();
- if (resLastMs < b1 || resLastMs > b2) {
- continue;
- }
- }
-
+ }
+
+ size_t spaceLeft = opts.SizeLimit;
+ for (const TTrackEntry& entry : Tracks) {
+ const TTrack* track = entry.Track;
+ // Filter out tracks that are not in sample
+ if (filterTotal) {
+ double resTotalMs = double(entry.ResTotal) * 1000.0 / NHPTimer::GetClockRate();
+ if (resTotalMs < b1 || resTotalMs > b2) {
+ continue;
+ }
+ } else {
+ double resLastMs = double(entry.ResLast) * 1000.0 / NHPTimer::GetClockRate();
+ if (resLastMs < b1 || resLastMs > b2) {
+ continue;
+ }
+ }
+
NLWTrace::TTrackLog tl;
for (TTrackIter i = TTrackTr::begin(*track), e = TTrackTr::end(*track); i != e; ++i) {
- const NLWTrace::TLogItem& item = *i;
+ const NLWTrace::TLogItem& item = *i;
const auto threadId = i->ThreadId;
tl.Items.push_back(NLWTrace::TTrackLog::TItem(threadId, item));
- }
+ }
reader.Push(0, tl);
- if (spaceLeft) {
- spaceLeft--;
- if (!spaceLeft) {
- break;
- }
- }
- }
- }
-};
-
-// Track classification tree
-class TPatternTree {
-public:
- // Per-node classifier by probe name
- class TClassifyByProbe : public TClassifier {
- private:
+ if (spaceLeft) {
+ spaceLeft--;
+ if (!spaceLeft) {
+ break;
+ }
+ }
+ }
+ }
+};
+
+// Track classification tree
+class TPatternTree {
+public:
+ // Per-node classifier by probe name
+ class TClassifyByProbe : public TClassifier {
+ private:
using TChildren = THashMap<NLWTrace::TProbe*, TPatternNode>;
- TChildren Children;
+ TChildren Children;
TVector<TChildren::value_type*> SortedChildren;
- public:
- explicit TClassifyByProbe(TPatternNode* node)
- : TClassifier(node, NT_PROBE)
- {}
-
- TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
- {
- Y_UNUSED(track);
- const NLWTrace::TLogItem& item = *cur;
- TPatternNode* node = &Children[item.Probe];
- node->Name = "/" + GetProbeName(item.Probe);
- node->Desc.Type = NT_PROBE;
- node->Desc.Probe = item.Probe;
- return node;
- }
-
- void Accept(IVisitor* visitor) override
- {
- if (SortedChildren.size() != Children.size()) {
- SortedChildren.clear();
- SortedChildren.reserve(Children.size());
- for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
- SortedChildren.push_back(&*i);
- }
- Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
- NLWTrace::TProbe* lp = lhs->first;
- NLWTrace::TProbe* rp = rhs->first;
- if (int cmp = strcmp(lp->Event.GetProvider(), rp->Event.GetProvider())) {
- return cmp < 0;
- }
- return strcmp(lp->Event.Name, rp->Event.Name) < 0;
- });
- }
- for (auto* kv : SortedChildren) {
- visitor->Visit(&kv->second);
- }
- }
-
- bool IsLeaf() override { return Children.empty(); }
- };
-
- // Per-node classifier by probe param value
- class TClassifyByParam : public TClassifier {
- private:
- size_t Rollbacks; // How many items should we look back in track to locate probe
+ public:
+ explicit TClassifyByProbe(TPatternNode* node)
+ : TClassifier(node, NT_PROBE)
+ {}
+
+ TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
+ {
+ Y_UNUSED(track);
+ const NLWTrace::TLogItem& item = *cur;
+ TPatternNode* node = &Children[item.Probe];
+ node->Name = "/" + GetProbeName(item.Probe);
+ node->Desc.Type = NT_PROBE;
+ node->Desc.Probe = item.Probe;
+ return node;
+ }
+
+ void Accept(IVisitor* visitor) override
+ {
+ if (SortedChildren.size() != Children.size()) {
+ SortedChildren.clear();
+ SortedChildren.reserve(Children.size());
+ for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
+ SortedChildren.push_back(&*i);
+ }
+ Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
+ NLWTrace::TProbe* lp = lhs->first;
+ NLWTrace::TProbe* rp = rhs->first;
+ if (int cmp = strcmp(lp->Event.GetProvider(), rp->Event.GetProvider())) {
+ return cmp < 0;
+ }
+ return strcmp(lp->Event.Name, rp->Event.Name) < 0;
+ });
+ }
+ for (auto* kv : SortedChildren) {
+ visitor->Visit(&kv->second);
+ }
+ }
+
+ bool IsLeaf() override { return Children.empty(); }
+ };
+
+ // Per-node classifier by probe param value
+ class TClassifyByParam : public TClassifier {
+ private:
+ size_t Rollbacks; // How many items should we look back in track to locate probe
TString ParamName;
using TChildren = THashMap<TString, TPatternNode>;
- TChildren Children;
+ TChildren Children;
TVector<TChildren::value_type*> SortedChildren;
- public:
+ public:
TClassifyByParam(TPatternNode* node, size_t rollbacks, const TString& paramName)
- : TClassifier(node, NT_PARAM, true)
- , Rollbacks(rollbacks)
- , ParamName(paramName)
- {}
-
- TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
- {
- WWW_CHECK((i64)Rollbacks >= 0 && std::distance(TTrackTr::begin(track), cur) >= (i64)Rollbacks, "wrong rollbacks in node '%s'",
+ : TClassifier(node, NT_PARAM, true)
+ , Rollbacks(rollbacks)
+ , ParamName(paramName)
+ {}
+
+ TPatternNode* Classify(TTrackIter cur, const TTrack& track) override
+ {
+ WWW_CHECK((i64)Rollbacks >= 0 && std::distance(TTrackTr::begin(track), cur) >= (i64)Rollbacks, "wrong rollbacks in node '%s'",
Node->GetPath().data());
- const NLWTrace::TLogItem& item = *(cur - Rollbacks);
- WWW_CHECK(item.SavedParamsCount > 0, "classify by params on probe w/o param loggging in node '%s'",
+ const NLWTrace::TLogItem& item = *(cur - Rollbacks);
+ WWW_CHECK(item.SavedParamsCount > 0, "classify by params on probe w/o param loggging in node '%s'",
Node->GetPath().data());
TString paramValues[LWTRACE_MAX_PARAMS];
TString* paramValue = nullptr;
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (item.Probe->Event.Signature.ParamNames[pi] == ParamName) {
- paramValue = &paramValues[pi];
- }
- }
- WWW_CHECK(paramValue, "param '%s' not found in probe '%s' at path '%s'",
+ item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
+ for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
+ if (item.Probe->Event.Signature.ParamNames[pi] == ParamName) {
+ paramValue = &paramValues[pi];
+ }
+ }
+ WWW_CHECK(paramValue, "param '%s' not found in probe '%s' at path '%s'",
ParamName.data(), GetProbeName(item.Probe).data(), Node->GetPath().data());
-
- TPatternNode* node = &Children[*paramValue];
- // Path example: "//Provider1.Probe1/Provider2.Probe2@1.xxx=123@2.type=harakiri"
- node->Name = "@" + ToString(Rollbacks) + "." + ParamName + "=" + *paramValue;
- node->Desc.Type = NT_PARAM;
- node->Desc.Rollbacks = Rollbacks;
- node->Desc.ParamName = ParamName;
- node->Desc.ParamValue = *paramValue;
- return node;
- }
-
- void Accept(IVisitor* visitor) override
- {
- if (SortedChildren.size() != Children.size()) {
- SortedChildren.clear();
- SortedChildren.reserve(Children.size());
- for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
- SortedChildren.push_back(&*i);
- }
- Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
- return lhs->first < rhs->first;
- });
- }
- for (auto* kv : SortedChildren) {
- visitor->Visit(&kv->second);
- }
- }
-
- bool IsLeaf() override { return Children.empty(); }
- };
-private:
- TPatternNode Root;
+
+ TPatternNode* node = &Children[*paramValue];
+ // Path example: "//Provider1.Probe1/Provider2.Probe2@1.xxx=123@2.type=harakiri"
+ node->Name = "@" + ToString(Rollbacks) + "." + ParamName + "=" + *paramValue;
+ node->Desc.Type = NT_PARAM;
+ node->Desc.Rollbacks = Rollbacks;
+ node->Desc.ParamName = ParamName;
+ node->Desc.ParamValue = *paramValue;
+ return node;
+ }
+
+ void Accept(IVisitor* visitor) override
+ {
+ if (SortedChildren.size() != Children.size()) {
+ SortedChildren.clear();
+ SortedChildren.reserve(Children.size());
+ for (auto i = Children.begin(), e = Children.end(); i != e; ++i) {
+ SortedChildren.push_back(&*i);
+ }
+ Sort(SortedChildren, [] (TChildren::value_type* lhs, TChildren::value_type* rhs) {
+ return lhs->first < rhs->first;
+ });
+ }
+ for (auto* kv : SortedChildren) {
+ visitor->Visit(&kv->second);
+ }
+ }
+
+ bool IsLeaf() override { return Children.empty(); }
+ };
+private:
+ TPatternNode Root;
THashMap<TString, std::pair<size_t, TString>> ParamClassifiers; // path -> (rollbacks, param)
TString SelectedPattern;
- TPatternNode* SelectedNode = nullptr;
+ TPatternNode* SelectedNode = nullptr;
TVector<ui64> Timeline; // Just to avoid reallocations
-public:
- TPatternTree(const TCgiParameters& e)
- {
+public:
+ TPatternTree(const TCgiParameters& e)
+ {
for (const TString& cl : Subvalues(e, "classify")) {
- size_t at = cl.find_last_of('@');
+ size_t at = cl.find_last_of('@');
if (at != TString::npos) {
- size_t dot = cl.find('.', at + 1);
+ size_t dot = cl.find('.', at + 1);
if (dot != TString::npos) {
- size_t rollbacks = FromString<size_t>(cl.substr(at + 1, dot - at - 1));
- ParamClassifiers[cl.substr(0, at)] = std::make_pair(rollbacks, cl.substr(dot + 1));
- }
- }
- }
- SelectedPattern = e.Get("pattern");
- InitNode(&Root, nullptr);
- }
-
- TPatternNode* GetSelectedNode()
- {
- return SelectedNode;
- }
-
- NAnalytics::TTable GetSelectedTable()
- {
- if (SelectedNode) {
- return SelectedNode->GetTable();
- } else {
- return NAnalytics::TTable();
- }
- }
-
+ size_t rollbacks = FromString<size_t>(cl.substr(at + 1, dot - at - 1));
+ ParamClassifiers[cl.substr(0, at)] = std::make_pair(rollbacks, cl.substr(dot + 1));
+ }
+ }
+ }
+ SelectedPattern = e.Get("pattern");
+ InitNode(&Root, nullptr);
+ }
+
+ TPatternNode* GetSelectedNode()
+ {
+ return SelectedNode;
+ }
+
+ NAnalytics::TTable GetSelectedTable()
+ {
+ if (SelectedNode) {
+ return SelectedNode->GetTable();
+ } else {
+ return NAnalytics::TTable();
+ }
+ }
+
template <typename TReader>
void OutputSelectedSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader)
- {
- if (SelectedNode) {
+ {
+ if (SelectedNode) {
SelectedNode->OutputSample(bn, b1, b2, opts, reader);
- }
- }
-
- // Register track in given node
+ }
+ }
+
+ // Register track in given node
void AddTrackToNode(TPatternNode* node, TTrack& track, ui64 resTotal, TVector<ui64>& timeline)
- {
- if (!SelectedNode) {
- if (node->GetPath() == SelectedPattern) {
- SelectedNode = node;
- }
- }
-
- // Counting
- node->TrackCount++;
-
- // Resource total
- node->ResTotalSum += resTotal;
- node->ResTotalMax = Max(node->ResTotalMax, resTotal);
- node->ResTotalAll.push_back(resTotal);
-
- // Resource last
- ui64 resLast = 0;
- resLast = resTotal - (timeline.size() < 2? 0: timeline[timeline.size() - 2]);
- node->ResLastSum += resLast;
- node->ResLastMax = Max(node->ResLastMax, resLast);
- node->ResLastAll.push_back(resLast);
-
- // Timeline
- if (node->TimelineSum.size() < timeline.size()) {
- node->TimelineSum.resize(timeline.size());
- }
- for (size_t i = 0; i < timeline.size(); i++) {
- node->TimelineSum[i] += timeline[i];
- }
-
- if (node == SelectedNode && !timeline.empty()) {
- node->Slices.emplace_back();
- NAnalytics::TRow& row = node->Slices.back();
- ui64 prev = 0;
- for (size_t i = 0; i < timeline.size(); i++) {
- // Note that col names should go in lexicographical order
- // in the same way as slices go in pattern timeline
- double sliceMs = double(timeline[i] - prev) * 1000.0 / NHPTimer::GetClockRate();
- row[Sprintf("%09lu", i)] = sliceMs;
- prev = timeline[i];
- }
- }
-
- // Interlink node and track
- node->Tracks.emplace_back(&track, resTotal, resLast);
- track.LastNode = node;
- }
-
- bool CheckPattern(const char*& pi, const char* pe, TStringBuf str)
- {
- auto si = str.begin(), se = str.end();
- for (;pi != pe && si != se; ++pi, ++si) {
- if (*pi != *si) {
- return false;
- }
- }
- return si == se;
- }
-
-#define WWW_CHECK_PATTERN(str) if (!CheckPattern(pi, pe, (str))) { return false; }
-
+ {
+ if (!SelectedNode) {
+ if (node->GetPath() == SelectedPattern) {
+ SelectedNode = node;
+ }
+ }
+
+ // Counting
+ node->TrackCount++;
+
+ // Resource total
+ node->ResTotalSum += resTotal;
+ node->ResTotalMax = Max(node->ResTotalMax, resTotal);
+ node->ResTotalAll.push_back(resTotal);
+
+ // Resource last
+ ui64 resLast = 0;
+ resLast = resTotal - (timeline.size() < 2? 0: timeline[timeline.size() - 2]);
+ node->ResLastSum += resLast;
+ node->ResLastMax = Max(node->ResLastMax, resLast);
+ node->ResLastAll.push_back(resLast);
+
+ // Timeline
+ if (node->TimelineSum.size() < timeline.size()) {
+ node->TimelineSum.resize(timeline.size());
+ }
+ for (size_t i = 0; i < timeline.size(); i++) {
+ node->TimelineSum[i] += timeline[i];
+ }
+
+ if (node == SelectedNode && !timeline.empty()) {
+ node->Slices.emplace_back();
+ NAnalytics::TRow& row = node->Slices.back();
+ ui64 prev = 0;
+ for (size_t i = 0; i < timeline.size(); i++) {
+ // Note that col names should go in lexicographical order
+ // in the same way as slices go in pattern timeline
+ double sliceMs = double(timeline[i] - prev) * 1000.0 / NHPTimer::GetClockRate();
+ row[Sprintf("%09lu", i)] = sliceMs;
+ prev = timeline[i];
+ }
+ }
+
+ // Interlink node and track
+ node->Tracks.emplace_back(&track, resTotal, resLast);
+ track.LastNode = node;
+ }
+
+ bool CheckPattern(const char*& pi, const char* pe, TStringBuf str)
+ {
+ auto si = str.begin(), se = str.end();
+ for (;pi != pe && si != se; ++pi, ++si) {
+ if (*pi != *si) {
+ return false;
+ }
+ }
+ return si == se;
+ }
+
+#define WWW_CHECK_PATTERN(str) if (!CheckPattern(pi, pe, (str))) { return false; }
+
bool MatchTrack(const TTrack& track, const TString& patternStr)
- {
+ {
const char* pi = patternStr.data();
const char* pe = pi + patternStr.size();
- WWW_CHECK_PATTERN("/");
- for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e; ++i) {
- if (pi == pe) {
- return true;
- }
- const NLWTrace::TLogItem& item = *i;
- WWW_CHECK_PATTERN("/");
- WWW_CHECK_PATTERN(item.Probe->Event.GetProvider());
- WWW_CHECK_PATTERN(".");
- WWW_CHECK_PATTERN(item.Probe->Event.Name);
- while (true) {
- if (pi == pe) {
- return true;
- }
- char c = *pi;
- if (c == '/') {
- break;
- } else if (c == '@') {
- pi++;
- // Parse rollbacks
- TStringBuf p(pi, pe);
- size_t dot = p.find('.');
- if (dot == TStringBuf::npos) {
- return false;
- }
- size_t rollbacks = 0;
- try {
- rollbacks = FromString<size_t>(p.substr(0, dot));
- } catch (...) {
- return false;
- }
-
- // Parse param name
- size_t equals = p.find('=', dot + 1);
- if (equals == TStringBuf::npos) {
- return false;
- }
- TStringBuf paramName = p.substr(dot + 1, equals - dot - 1);
-
- pi += equals + 1; // Advance to value
-
- // Check param value
- if ((i64)rollbacks < 0 || std::distance(TTrackTr::begin(track), i) < (i64)rollbacks) {
- return false;
- }
- const NLWTrace::TLogItem& mitem = *(i - rollbacks);
- if (mitem.SavedParamsCount == 0) {
- return false;
- }
+ WWW_CHECK_PATTERN("/");
+ for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e; ++i) {
+ if (pi == pe) {
+ return true;
+ }
+ const NLWTrace::TLogItem& item = *i;
+ WWW_CHECK_PATTERN("/");
+ WWW_CHECK_PATTERN(item.Probe->Event.GetProvider());
+ WWW_CHECK_PATTERN(".");
+ WWW_CHECK_PATTERN(item.Probe->Event.Name);
+ while (true) {
+ if (pi == pe) {
+ return true;
+ }
+ char c = *pi;
+ if (c == '/') {
+ break;
+ } else if (c == '@') {
+ pi++;
+ // Parse rollbacks
+ TStringBuf p(pi, pe);
+ size_t dot = p.find('.');
+ if (dot == TStringBuf::npos) {
+ return false;
+ }
+ size_t rollbacks = 0;
+ try {
+ rollbacks = FromString<size_t>(p.substr(0, dot));
+ } catch (...) {
+ return false;
+ }
+
+ // Parse param name
+ size_t equals = p.find('=', dot + 1);
+ if (equals == TStringBuf::npos) {
+ return false;
+ }
+ TStringBuf paramName = p.substr(dot + 1, equals - dot - 1);
+
+ pi += equals + 1; // Advance to value
+
+ // Check param value
+ if ((i64)rollbacks < 0 || std::distance(TTrackTr::begin(track), i) < (i64)rollbacks) {
+ return false;
+ }
+ const NLWTrace::TLogItem& mitem = *(i - rollbacks);
+ if (mitem.SavedParamsCount == 0) {
+ return false;
+ }
TString paramValues[LWTRACE_MAX_PARAMS];
TString* paramValue = nullptr;
- mitem.Probe->Event.Signature.SerializeParams(mitem.Params, paramValues);
- for (size_t pi = 0; pi < mitem.SavedParamsCount; pi++) {
- if (mitem.Probe->Event.Signature.ParamNames[pi] == paramName) {
- paramValue = &paramValues[pi];
- }
- }
- if (!paramValue) {
- return false;
- }
- WWW_CHECK_PATTERN(*paramValue);
- } else {
- return false;
- }
- }
- }
- return true;
- }
-
-#undef WWW_CHECK_PATTERN
-
- // Push new track through pattern tree
- void AddTrack(TTrack& track)
- {
- // Truncate long tracks
- if (track.Items.size() > 50) {
- track.Items.resize(50);
- }
-
- if (SelectedPattern) {
- if (!MatchTrack(track, SelectedPattern)) {
- return;
- }
- }
-
- Timeline.clear();
- TPatternNode* node = &Root;
- AddTrackToNode(node, track, 0, Timeline);
- ui64 trackStart = TTrackTr::front(track).TimestampCycles;
- for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e;) {
- // Get or create child by classification
- TPatternNode* parent = node;
- node = node->Classifier->Classify(i, track);
- if (!node->Classifier) {
- InitNode(node, parent);
- }
-
- const NLWTrace::TLogItem& item = *i;
- ui64 resTotal = item.TimestampCycles - trackStart;
- if (i != TTrackTr::begin(track)) {
- Timeline.push_back(resTotal);
- }
- AddTrackToNode(node, track, resTotal, Timeline);
-
- // Move through track
- if (!node->Classifier->KeepHead) {
- ++i;
- }
- }
- }
-
- // Traverse pattern tree (the only way to extract data from it)
- template <class TOnNode, class TOnDescend, class TOnAscend>
- void Traverse(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
- {
- struct TVisitor : public IVisitor {
- TOnNode OnNode;
- TOnDescend OnDescend;
- TOnAscend OnAscend;
- TVisitor(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
- : OnNode(onNode)
- , OnDescend(onDescend)
- , OnAscend(onAscend)
- {}
- virtual void Visit(TPatternNode* node) override
- {
- OnNode(node);
- if (!node->Classifier->IsLeaf()) {
- OnDescend();
- node->Classifier->Accept(this);
- OnAscend();
- }
- }
- };
- TVisitor visitor(std::move(onNode), std::move(onDescend), std::move(onAscend));
- visitor.Visit(&Root);
- }
-
- TPatternNode* GetRoot()
- {
- return &Root;
- }
-
-private:
- void InitNode(TPatternNode* node, TPatternNode* parent)
- {
- node->Parent = parent;
- auto iter = ParamClassifiers.find(node->GetPath());
- if (iter != ParamClassifiers.end()) {
- node->Classifier.Reset(new TClassifyByParam(node, iter->second.first, iter->second.second));
- } else {
- node->Classifier.Reset(new TClassifyByProbe(node));
- }
- }
-};
-
-class TLogTrackExtractor: public TLogFilter {
-private:
- // Data storage
+ mitem.Probe->Event.Signature.SerializeParams(mitem.Params, paramValues);
+ for (size_t pi = 0; pi < mitem.SavedParamsCount; pi++) {
+ if (mitem.Probe->Event.Signature.ParamNames[pi] == paramName) {
+ paramValue = &paramValues[pi];
+ }
+ }
+ if (!paramValue) {
+ return false;
+ }
+ WWW_CHECK_PATTERN(*paramValue);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+#undef WWW_CHECK_PATTERN
+
+ // Push new track through pattern tree
+ void AddTrack(TTrack& track)
+ {
+ // Truncate long tracks
+ if (track.Items.size() > 50) {
+ track.Items.resize(50);
+ }
+
+ if (SelectedPattern) {
+ if (!MatchTrack(track, SelectedPattern)) {
+ return;
+ }
+ }
+
+ Timeline.clear();
+ TPatternNode* node = &Root;
+ AddTrackToNode(node, track, 0, Timeline);
+ ui64 trackStart = TTrackTr::front(track).TimestampCycles;
+ for (TTrackIter i = TTrackTr::begin(track), e = TTrackTr::end(track); i != e;) {
+ // Get or create child by classification
+ TPatternNode* parent = node;
+ node = node->Classifier->Classify(i, track);
+ if (!node->Classifier) {
+ InitNode(node, parent);
+ }
+
+ const NLWTrace::TLogItem& item = *i;
+ ui64 resTotal = item.TimestampCycles - trackStart;
+ if (i != TTrackTr::begin(track)) {
+ Timeline.push_back(resTotal);
+ }
+ AddTrackToNode(node, track, resTotal, Timeline);
+
+ // Move through track
+ if (!node->Classifier->KeepHead) {
+ ++i;
+ }
+ }
+ }
+
+ // Traverse pattern tree (the only way to extract data from it)
+ template <class TOnNode, class TOnDescend, class TOnAscend>
+ void Traverse(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
+ {
+ struct TVisitor : public IVisitor {
+ TOnNode OnNode;
+ TOnDescend OnDescend;
+ TOnAscend OnAscend;
+ TVisitor(TOnNode&& onNode, TOnDescend&& onDescend, TOnAscend&& onAscend)
+ : OnNode(onNode)
+ , OnDescend(onDescend)
+ , OnAscend(onAscend)
+ {}
+ virtual void Visit(TPatternNode* node) override
+ {
+ OnNode(node);
+ if (!node->Classifier->IsLeaf()) {
+ OnDescend();
+ node->Classifier->Accept(this);
+ OnAscend();
+ }
+ }
+ };
+ TVisitor visitor(std::move(onNode), std::move(onDescend), std::move(onAscend));
+ visitor.Visit(&Root);
+ }
+
+ TPatternNode* GetRoot()
+ {
+ return &Root;
+ }
+
+private:
+ void InitNode(TPatternNode* node, TPatternNode* parent)
+ {
+ node->Parent = parent;
+ auto iter = ParamClassifiers.find(node->GetPath());
+ if (iter != ParamClassifiers.end()) {
+ node->Classifier.Reset(new TClassifyByParam(node, iter->second.first, iter->second.second));
+ } else {
+ node->Classifier.Reset(new TClassifyByProbe(node));
+ }
+ }
+};
+
+class TLogTrackExtractor: public TLogFilter {
+private:
+ // Data storage
TMultiMap<ui64, std::pair<TThread::TId, NLWTrace::TLogItem>> Items;
TVector<NLWTrace::TTrackLog> Depot;
-
- // Data refs organized in tracks
+
+ // Data refs organized in tracks
THashMap<TString, TTrack> Tracks;
TVector<TTrack> TracksFromDepot;
-
- // Analysis
+
+ // Analysis
TVector<TString> GroupBy;
THashSet<TString> TrackIds; // The same content as in GroupBy
- TTimestampCutter CutTs;
- TPatternTree Tree;
-public:
+ TTimestampCutter CutTs;
+ TPatternTree Tree;
+public:
TLogTrackExtractor(const TCgiParameters& e, const TVector<TString>& filters, const TVector<TString>& groupBy)
- : TLogFilter(filters)
- , CutTs(true) // Always cut input data for tracks
- , Tree(e)
- {
+ : TLogFilter(filters)
+ , CutTs(true) // Always cut input data for tracks
+ , Tree(e)
+ {
for (const TString& groupParam : groupBy) {
- GroupBy.push_back(groupParam);
- TrackIds.insert(groupParam);
- }
- }
-
- // For reading lwtrace log (input point for all data)
- void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- CutTs.Push(tid, item);
- if (Filter(item)) {
- Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
- }
- }
-
- // For reading lwtrace depot (input point for all data)
- void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
- {
- if (Filter(tl)) {
- Depot.emplace_back(tl);
- }
- }
-
- // Analyze logs that have been read
- void Run()
- {
- RunImplLog();
- RunImplDepot();
- }
-
- void RunImplLog()
- {
- // Create tracks by filling them with lwtrace items in order of occurance time
- for (auto& kv : Items) {
- AddItemToTrack(kv.second.first, kv.second.second);
- }
- // Push tracks throught pattern tree
- for (auto& kv : Tracks) {
- TTrack& track = kv.second;
- track.TrackId = kv.first;
- Tree.AddTrack(track);
- }
- }
-
- void RunImplDepot()
- {
- // Create tracks from depot
- // OPTIMIZE[serxa]: this convertion is not necessary, done just to keep things simple
- for (NLWTrace::TTrackLog& tl : Depot) {
- TTrack& track = TracksFromDepot.emplace_back();
- track.TrackId = ToString(tl.Id);
- for (const NLWTrace::TTrackLog::TItem& i : tl.Items) {
- track.Items.emplace_back(i.ThreadId, i);
- }
- }
- for (TTrack& t : TracksFromDepot) {
- Tree.AddTrack(t);
- }
- }
-
- // Selected node distribution
- NAnalytics::TTable Distribution(const TString& bn, const TString& b1Str, const TString& b2Str, const TString& widthStr)
- {
- using namespace NAnalytics;
-
- const NAnalytics::TTable& inputTable = Tree.GetSelectedTable();
- double b1 = b1Str? FromString<double>(b1Str): MinValue(bn, inputTable);
- double b2 = b2Str? FromString<double>(b2Str): MaxValue(bn, inputTable);
- if (isfinite(b1) && isfinite(b2)) {
- WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
- double width = widthStr? FromString<double>(widthStr): 99;
- double dx = (b2 - b1) / width;
- if (!(dx > 0)) {
- dx = 1.0;
- }
- return HistogramAll(inputTable, bn, b1, b2, dx);
- } else {
- // Empty table -- it's ok -- leave data table empty
- return NAnalytics::TTable();
- }
- }
-
- // Selected sample
+ GroupBy.push_back(groupParam);
+ TrackIds.insert(groupParam);
+ }
+ }
+
+ // For reading lwtrace log (input point for all data)
+ void Push(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ CutTs.Push(tid, item);
+ if (Filter(item)) {
+ Items.emplace(item.TimestampCycles, std::make_pair(tid, item));
+ }
+ }
+
+ // For reading lwtrace depot (input point for all data)
+ void Push(TThread::TId, const NLWTrace::TTrackLog& tl)
+ {
+ if (Filter(tl)) {
+ Depot.emplace_back(tl);
+ }
+ }
+
+ // Analyze logs that have been read
+ void Run()
+ {
+ RunImplLog();
+ RunImplDepot();
+ }
+
+ void RunImplLog()
+ {
+ // Create tracks by filling them with lwtrace items in order of occurance time
+ for (auto& kv : Items) {
+ AddItemToTrack(kv.second.first, kv.second.second);
+ }
+ // Push tracks throught pattern tree
+ for (auto& kv : Tracks) {
+ TTrack& track = kv.second;
+ track.TrackId = kv.first;
+ Tree.AddTrack(track);
+ }
+ }
+
+ void RunImplDepot()
+ {
+ // Create tracks from depot
+ // OPTIMIZE[serxa]: this convertion is not necessary, done just to keep things simple
+ for (NLWTrace::TTrackLog& tl : Depot) {
+ TTrack& track = TracksFromDepot.emplace_back();
+ track.TrackId = ToString(tl.Id);
+ for (const NLWTrace::TTrackLog::TItem& i : tl.Items) {
+ track.Items.emplace_back(i.ThreadId, i);
+ }
+ }
+ for (TTrack& t : TracksFromDepot) {
+ Tree.AddTrack(t);
+ }
+ }
+
+ // Selected node distribution
+ NAnalytics::TTable Distribution(const TString& bn, const TString& b1Str, const TString& b2Str, const TString& widthStr)
+ {
+ using namespace NAnalytics;
+
+ const NAnalytics::TTable& inputTable = Tree.GetSelectedTable();
+ double b1 = b1Str? FromString<double>(b1Str): MinValue(bn, inputTable);
+ double b2 = b2Str? FromString<double>(b2Str): MaxValue(bn, inputTable);
+ if (isfinite(b1) && isfinite(b2)) {
+ WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
+ double width = widthStr? FromString<double>(widthStr): 99;
+ double dx = (b2 - b1) / width;
+ if (!(dx > 0)) {
+ dx = 1.0;
+ }
+ return HistogramAll(inputTable, bn, b1, b2, dx);
+ } else {
+ // Empty table -- it's ok -- leave data table empty
+ return NAnalytics::TTable();
+ }
+ }
+
+ // Selected sample
template <typename TReader>
void OutputSample(const TString& bn, double b1, double b2, const TSampleOpts& opts, TReader& reader)
- {
+ {
Tree.OutputSelectedSample(bn, b1, b2, opts, reader);
- }
-
- // Tabular representation of tracks data
+ }
+
+ // Tabular representation of tracks data
void OutputTable(IOutputStream& os, const TCgiParameters& e)
- {
- ui64 tracksTotal = Tree.GetRoot()->TrackCount;
-
- double maxAvgResTotal = 0;
- double maxMaxResTotal = 0;
- Tree.Traverse([&] (TPatternNode* node) {
- if (node->TrackCount > 0) {
- maxAvgResTotal = Max(maxAvgResTotal, double(node->ResTotalSum) / node->TrackCount);
- maxMaxResTotal = Max(maxMaxResTotal, double(node->ResTotalMax));
- Sort(node->ResTotalAll);
- Sort(node->ResLastAll);
- }
- }, [&] () { // On descend
- }, [&] () { // On ascend
- });
- double maxTime = Min(maxMaxResTotal, 1.25 * maxAvgResTotal);
-
- double percentile = e.Get("ile")? FromString<double>(e.Get("ile")): 90;
- WWW_CHECK(percentile >= 0.0 && percentile <= 100.0, "wrong percentile: %lf", percentile);
-
- ui64 row = 0;
+ {
+ ui64 tracksTotal = Tree.GetRoot()->TrackCount;
+
+ double maxAvgResTotal = 0;
+ double maxMaxResTotal = 0;
+ Tree.Traverse([&] (TPatternNode* node) {
+ if (node->TrackCount > 0) {
+ maxAvgResTotal = Max(maxAvgResTotal, double(node->ResTotalSum) / node->TrackCount);
+ maxMaxResTotal = Max(maxMaxResTotal, double(node->ResTotalMax));
+ Sort(node->ResTotalAll);
+ Sort(node->ResLastAll);
+ }
+ }, [&] () { // On descend
+ }, [&] () { // On ascend
+ });
+ double maxTime = Min(maxMaxResTotal, 1.25 * maxAvgResTotal);
+
+ double percentile = e.Get("ile")? FromString<double>(e.Get("ile")): 90;
+ WWW_CHECK(percentile >= 0.0 && percentile <= 100.0, "wrong percentile: %lf", percentile);
+
+ ui64 row = 0;
TVector<ui64> chain;
- HTML(os) {
- TABLE_CLASS("tracks-tree") {
- TABLEHEAD() {
- os << "<tr>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">#</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Pattern</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">";
- DIV_CLASS("rotate") { os << "Track Count"; }
- os << "</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Share</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Total, ms</td>";
- os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Last, ms</td>";
- os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" class=\"timelinehead\" align=\"center\">Global Timeline</td>";
- os << "</tr><tr>";
- TABLEH() DIV_CLASS("rotate") { os << "Absolute"; }
- TABLEH() DIV_CLASS("rotate") { os << "Relative"; }
- TABLEH() DIV_CLASS("rotate") { os << "Average"; }
- TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
- TABLEH() DIV_CLASS("rotate") { os << "Average"; }
- TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
- os << "</tr>";
- }
- TABLEBODY() {
- if (tracksTotal == 0) {
- return;
- }
- Tree.Traverse([&] (TPatternNode* node) {
+ HTML(os) {
+ TABLE_CLASS("tracks-tree") {
+ TABLEHEAD() {
+ os << "<tr>";
+ os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">#</td>";
+ os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Pattern</td>";
+ os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">";
+ DIV_CLASS("rotate") { os << "Track Count"; }
+ os << "</td>";
+ os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Share</td>";
+ os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Total, ms</td>";
+ os << "<td colspan=\"2\" style=\"vertical-align:bottom\" align=\"center\">Last, ms</td>";
+ os << "<td rowspan=\"2\" style=\"vertical-align:bottom\" class=\"timelinehead\" align=\"center\">Global Timeline</td>";
+ os << "</tr><tr>";
+ TABLEH() DIV_CLASS("rotate") { os << "Absolute"; }
+ TABLEH() DIV_CLASS("rotate") { os << "Relative"; }
+ TABLEH() DIV_CLASS("rotate") { os << "Average"; }
+ TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
+ TABLEH() DIV_CLASS("rotate") { os << "Average"; }
+ TABLEH() DIV_CLASS("rotate") { os << percentile << "%-ile"; }
+ os << "</tr>";
+ }
+ TABLEBODY() {
+ if (tracksTotal == 0) {
+ return;
+ }
+ Tree.Traverse([&] (TPatternNode* node) {
TString parentClass;
- if (!chain.empty()) {
- parentClass = " treegrid-parent-" + ToString(chain.back());
- }
+ if (!chain.empty()) {
+ parentClass = " treegrid-parent-" + ToString(chain.back());
+ }
TString selectedClass;
- if (e.Get("pattern") == node->GetPath()) {
- selectedClass = " danger";
- }
- TABLER_CLASS("treegrid-" + ToString(++row) + parentClass + selectedClass) {
- // Counting
- ui64 tracksParent = node->Parent? node->Parent->TrackCount: tracksTotal;
- double absShare = double(node->TrackCount) * 100 / tracksTotal;
- double relShare = double(node->TrackCount) * 100 / tracksParent;
-
- // Resource total
- double avgResTotal = double(node->ResTotalSum) / node->TrackCount;
- size_t ileResTotalIdx = node->ResTotalAll.size() * percentile / 100;
- if (ileResTotalIdx > 0) {
- ileResTotalIdx--;
- }
- double ileResTotal = double(ileResTotalIdx >= node->ResTotalAll.size()? 0: node->ResTotalAll[ileResTotalIdx]);
- double avgResTotalMs = avgResTotal * 1000.0 / NHPTimer::GetClockRate();
- double ileResTotalMs = ileResTotal * 1000.0 / NHPTimer::GetClockRate();
-
- // Resource last
- double avgResLast = double(node->ResLastSum) / node->TrackCount;
- size_t ileResLastIdx = node->ResLastAll.size() * percentile / 100;
- if (ileResLastIdx > 0) {
- ileResLastIdx--;
- }
- double ileResLast = double(ileResLastIdx >= node->ResLastAll.size()? 0: node->ResLastAll[ileResLastIdx]);
- double avgResLastMs = avgResLast * 1000.0 / NHPTimer::GetClockRate();
- double ileResLastMs = ileResLast * 1000.0 / NHPTimer::GetClockRate();
-
- // Output
- TABLED() { os << row; }
- TABLED_CLASS("treegrid-element") { OutputPattern(os, e, node); }
- TABLED() { os << node->TrackCount; }
- TABLED() { OutputShare(os, absShare); }
- TABLED() { OutputShare(os, relShare); }
- TABLED() { os << FormatFloat(avgResTotalMs); }
- TABLED() { os << FormatFloat(ileResTotalMs); }
- TABLED() { os << FormatFloat(avgResLastMs); }
- TABLED() { os << FormatFloat(ileResLastMs); }
- TABLED() { OutputTimeline(os, MakeTimeline(node), maxTime); }
- }
- }, [&] () { // On descend
- chain.push_back(row);
- }, [&] () { // On ascend
- chain.pop_back();
- });
- }
- }
- }
- }
-
- // Chromium-compatible trace representation of tracks data
+ if (e.Get("pattern") == node->GetPath()) {
+ selectedClass = " danger";
+ }
+ TABLER_CLASS("treegrid-" + ToString(++row) + parentClass + selectedClass) {
+ // Counting
+ ui64 tracksParent = node->Parent? node->Parent->TrackCount: tracksTotal;
+ double absShare = double(node->TrackCount) * 100 / tracksTotal;
+ double relShare = double(node->TrackCount) * 100 / tracksParent;
+
+ // Resource total
+ double avgResTotal = double(node->ResTotalSum) / node->TrackCount;
+ size_t ileResTotalIdx = node->ResTotalAll.size() * percentile / 100;
+ if (ileResTotalIdx > 0) {
+ ileResTotalIdx--;
+ }
+ double ileResTotal = double(ileResTotalIdx >= node->ResTotalAll.size()? 0: node->ResTotalAll[ileResTotalIdx]);
+ double avgResTotalMs = avgResTotal * 1000.0 / NHPTimer::GetClockRate();
+ double ileResTotalMs = ileResTotal * 1000.0 / NHPTimer::GetClockRate();
+
+ // Resource last
+ double avgResLast = double(node->ResLastSum) / node->TrackCount;
+ size_t ileResLastIdx = node->ResLastAll.size() * percentile / 100;
+ if (ileResLastIdx > 0) {
+ ileResLastIdx--;
+ }
+ double ileResLast = double(ileResLastIdx >= node->ResLastAll.size()? 0: node->ResLastAll[ileResLastIdx]);
+ double avgResLastMs = avgResLast * 1000.0 / NHPTimer::GetClockRate();
+ double ileResLastMs = ileResLast * 1000.0 / NHPTimer::GetClockRate();
+
+ // Output
+ TABLED() { os << row; }
+ TABLED_CLASS("treegrid-element") { OutputPattern(os, e, node); }
+ TABLED() { os << node->TrackCount; }
+ TABLED() { OutputShare(os, absShare); }
+ TABLED() { OutputShare(os, relShare); }
+ TABLED() { os << FormatFloat(avgResTotalMs); }
+ TABLED() { os << FormatFloat(ileResTotalMs); }
+ TABLED() { os << FormatFloat(avgResLastMs); }
+ TABLED() { os << FormatFloat(ileResLastMs); }
+ TABLED() { OutputTimeline(os, MakeTimeline(node), maxTime); }
+ }
+ }, [&] () { // On descend
+ chain.push_back(row);
+ }, [&] () { // On ascend
+ chain.pop_back();
+ });
+ }
+ }
+ }
+ }
+
+ // Chromium-compatible trace representation of tracks data
void OutputChromeTrace(IOutputStream& os, const TCgiParameters& e)
- {
- Y_UNUSED(e);
- TChromeTrace tr;
- for (TPatternNode::TTrackEntry& entry: Tree.GetRoot()->Tracks) {
- TTrack* track = entry.Track;
- auto first = TTrackTr::begin(*track);
- auto last = TTrackTr::rbegin(*track);
-
+ {
+ Y_UNUSED(e);
+ TChromeTrace tr;
+ for (TPatternNode::TTrackEntry& entry: Tree.GetRoot()->Tracks) {
+ TTrack* track = entry.Track;
+ auto first = TTrackTr::begin(*track);
+ auto last = TTrackTr::rbegin(*track);
+
TString name = track->LastNode->GetPath();
-
- const NLWTrace::TLogItem& firstItem = *first;
- TThread::TId firstTid = first->ThreadId;
- tr.Add(firstTid, firstItem.TimestampCycles, "b", "track", nullptr, name, track->TrackId);
-
- for (auto cur = TTrackTr::begin(*track), end = TTrackTr::end(*track); cur != end; ++cur) {
- const NLWTrace::TLogItem& item = *cur;
-
- tr.Add(cur->ThreadId, item.TimestampCycles, "i", "event", &item, GetProbeName(item.Probe));
-
+
+ const NLWTrace::TLogItem& firstItem = *first;
+ TThread::TId firstTid = first->ThreadId;
+ tr.Add(firstTid, firstItem.TimestampCycles, "b", "track", nullptr, name, track->TrackId);
+
+ for (auto cur = TTrackTr::begin(*track), end = TTrackTr::end(*track); cur != end; ++cur) {
+ const NLWTrace::TLogItem& item = *cur;
+
+ tr.Add(cur->ThreadId, item.TimestampCycles, "i", "event", &item, GetProbeName(item.Probe));
+
TString sliceName = GetProbeName(item.Probe);
-
- auto next = cur + 1;
- if (next != end) {
- const NLWTrace::TLogItem& nextItem = *next;
- tr.Add(cur->ThreadId, item.TimestampCycles, "b", "track", &item, sliceName, track->TrackId);
- tr.Add(next->ThreadId, nextItem.TimestampCycles, "e", "track", &nextItem, sliceName, track->TrackId);
- } else {
- tr.Add(cur->ThreadId, item.TimestampCycles, "n", "track", &item, sliceName, track->TrackId);
- }
- }
-
- const NLWTrace::TLogItem& lastItem = *last;
- tr.Add(last->ThreadId, lastItem.TimestampCycles, "e", "track", nullptr, name, track->TrackId);
- }
- tr.Output(os);
- }
-
+
+ auto next = cur + 1;
+ if (next != end) {
+ const NLWTrace::TLogItem& nextItem = *next;
+ tr.Add(cur->ThreadId, item.TimestampCycles, "b", "track", &item, sliceName, track->TrackId);
+ tr.Add(next->ThreadId, nextItem.TimestampCycles, "e", "track", &nextItem, sliceName, track->TrackId);
+ } else {
+ tr.Add(cur->ThreadId, item.TimestampCycles, "n", "track", &item, sliceName, track->TrackId);
+ }
+ }
+
+ const NLWTrace::TLogItem& lastItem = *last;
+ tr.Add(last->ThreadId, lastItem.TimestampCycles, "e", "track", nullptr, name, track->TrackId);
+ }
+ tr.Output(os);
+ }
+
void OutputSliceCovarianceMatrix(IOutputStream& os, const TCgiParameters& e)
- {
- Y_UNUSED(e);
- TPatternNode* node = Tree.GetSelectedNode();
- if (!node) {
- return;
- }
-
- NAnalytics::TMatrix covMatrix = NAnalytics::CovarianceMatrix(node->Slices);
- double var = covMatrix.CellSum();
-
- double covMax = 0.0;
- for (double x : covMatrix) {
- if (covMax < x) {
- covMax = x;
- }
- }
- double dangerCov = covMax * 0.9 * 0.9;
- double warnCov = covMax * 0.5 * 0.5;
-
- HTML(os) {
- TABLE() {
- TTimeline timeline = MakeTimeline(node);
- TABLEHEAD() TABLER() {
- TABLED();
- for (auto& e : timeline) TABLED() {
- TPatternNode* subnode = e.first;
- os << subnode->Name;
- }
- }
-
- auto tl = timeline.begin();
- TABLEBODY() for (size_t row = 0; row < covMatrix.Rows; row++) TABLER() {
- TABLEH() {
- if (tl != timeline.end()) {
- TPatternNode* subnode = tl->first;
- os << subnode->Name;
- ++tl;
- }
- }
-
- for (size_t col = 0; col < covMatrix.Cols; col++) {
- double cov = covMatrix.Cell(row, col);
- TString tdClass = (cov >= dangerCov? "danger": (cov >= warnCov? "warning": ""));
- TABLED_CLASS(tdClass) {
- double sigmaX = (covMatrix.Cell(row, row) > 0? sqrt(covMatrix.Cell(row, row)): 0);
- double sigmaY = (covMatrix.Cell(col, col) > 0? sqrt(covMatrix.Cell(col, col)): 0);
- os << Sprintf("cov=%.3lf&nbsp;ms<sup>2</sup>&nbsp;(%.3lf&nbsp;ms) corr=%.1lf%% var_share=%.1lf%%",
- cov, sqrt(abs(cov)), cov * 100.0 / sigmaX / sigmaY, cov * 100.0 / var);
- }
- }
- }
- }
- }
- }
-
-private:
- TPatternNode* RollbackFind(TPatternNode* node)
- {
- for (;node != nullptr; node = node->Parent) {
- if (node->Desc.Type == NT_PROBE) {
- return node;
- }
- }
- return nullptr;
- }
-
+ {
+ Y_UNUSED(e);
+ TPatternNode* node = Tree.GetSelectedNode();
+ if (!node) {
+ return;
+ }
+
+ NAnalytics::TMatrix covMatrix = NAnalytics::CovarianceMatrix(node->Slices);
+ double var = covMatrix.CellSum();
+
+ double covMax = 0.0;
+ for (double x : covMatrix) {
+ if (covMax < x) {
+ covMax = x;
+ }
+ }
+ double dangerCov = covMax * 0.9 * 0.9;
+ double warnCov = covMax * 0.5 * 0.5;
+
+ HTML(os) {
+ TABLE() {
+ TTimeline timeline = MakeTimeline(node);
+ TABLEHEAD() TABLER() {
+ TABLED();
+ for (auto& e : timeline) TABLED() {
+ TPatternNode* subnode = e.first;
+ os << subnode->Name;
+ }
+ }
+
+ auto tl = timeline.begin();
+ TABLEBODY() for (size_t row = 0; row < covMatrix.Rows; row++) TABLER() {
+ TABLEH() {
+ if (tl != timeline.end()) {
+ TPatternNode* subnode = tl->first;
+ os << subnode->Name;
+ ++tl;
+ }
+ }
+
+ for (size_t col = 0; col < covMatrix.Cols; col++) {
+ double cov = covMatrix.Cell(row, col);
+ TString tdClass = (cov >= dangerCov? "danger": (cov >= warnCov? "warning": ""));
+ TABLED_CLASS(tdClass) {
+ double sigmaX = (covMatrix.Cell(row, row) > 0? sqrt(covMatrix.Cell(row, row)): 0);
+ double sigmaY = (covMatrix.Cell(col, col) > 0? sqrt(covMatrix.Cell(col, col)): 0);
+ os << Sprintf("cov=%.3lf&nbsp;ms<sup>2</sup>&nbsp;(%.3lf&nbsp;ms) corr=%.1lf%% var_share=%.1lf%%",
+ cov, sqrt(abs(cov)), cov * 100.0 / sigmaX / sigmaY, cov * 100.0 / var);
+ }
+ }
+ }
+ }
+ }
+ }
+
+private:
+ TPatternNode* RollbackFind(TPatternNode* node)
+ {
+ for (;node != nullptr; node = node->Parent) {
+ if (node->Desc.Type == NT_PROBE) {
+ return node;
+ }
+ }
+ return nullptr;
+ }
+
void OutputPattern(IOutputStream& os, const TCgiParameters& e, TPatternNode* node)
- {
- // Fill pattern name
+ {
+ // Fill pattern name
TString patternName;
TString patternTitle;
- switch (node->Desc.Type) {
- case NT_ROOT:
- patternName = "All Tracks";
- break;
- case NT_PROBE:
- patternTitle = GetProbeName(node->Desc.Probe);
- patternName = node->Desc.Probe->Event.Name;
- break;
- case NT_PARAM:
- patternName.append(node->Desc.ParamName + " = " + node->Desc.ParamValue);
- break;
- }
-
- os << "<a href=\"" << MakeUrl(e, {
- {"pattern", node->GetPath()},
- {"ptrn_anlz", e.Get("ptrn_anlz") ? e.Get("ptrn_anlz") : "resTotal"},
- {"linesfill", "y"},
- {"linessteps", "y"},
- {"pointsshow", "n"},
- {"sel_x1", e.Get("sel_x1") ? e.Get("sel_x1") : "0"},
- {"sel_x2", e.Get("sel_x2") ? e.Get("sel_x2") : "inf"}}) << "\""
- " title=\"" + patternTitle + "\">" << patternName << "</a>";
-
- // Add/remove node menu
- if (node->Desc.Type != NT_ROOT) {
- os << "<div class=\"dropdown pull-right\" style=\"display:inline-block\">";
- if (node->Desc.Type == NT_PARAM) {
- os<< "<button class=\"btn btn-xs btn-default\" type=\"button\""
- << "\" onClick=\"window.location.href='"
- << MakeUrlEraseSub(e, "classify", node->Parent->GetPath() + "@"
- + ToString(node->Desc.Rollbacks) + "." + node->Desc.ParamName)
- << "';\">"
- "<span class=\"glyphicon glyphicon-minus\"></span>"
- "</button>";
- }
- if (node->Classifier->GetChildType() != NT_PARAM) {
- os << "<button class=\"btn btn-xs btn-default dropdown-toggle\" type=\"button\""
- " data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"true\">"
- "<span class=\"glyphicon glyphicon-plus\"></span>"
- "</button>"
- "<ul class=\"dropdown-menu\">"
- "<li class=\"dropdown-header\">Classify by param:</li>";
- int rollbacks = 0;
- TPatternNode* probeNode = node;
- while (probeNode = RollbackFind(probeNode)) {
- const NLWTrace::TProbe* probe = probeNode->Desc.Probe;
- os << "<li class=\"dropdown-header\">" << GetProbeName(probe) << "</li>";
- const NLWTrace::TSignature* sgn = &probe->Event.Signature;
- for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
+ switch (node->Desc.Type) {
+ case NT_ROOT:
+ patternName = "All Tracks";
+ break;
+ case NT_PROBE:
+ patternTitle = GetProbeName(node->Desc.Probe);
+ patternName = node->Desc.Probe->Event.Name;
+ break;
+ case NT_PARAM:
+ patternName.append(node->Desc.ParamName + " = " + node->Desc.ParamValue);
+ break;
+ }
+
+ os << "<a href=\"" << MakeUrl(e, {
+ {"pattern", node->GetPath()},
+ {"ptrn_anlz", e.Get("ptrn_anlz") ? e.Get("ptrn_anlz") : "resTotal"},
+ {"linesfill", "y"},
+ {"linessteps", "y"},
+ {"pointsshow", "n"},
+ {"sel_x1", e.Get("sel_x1") ? e.Get("sel_x1") : "0"},
+ {"sel_x2", e.Get("sel_x2") ? e.Get("sel_x2") : "inf"}}) << "\""
+ " title=\"" + patternTitle + "\">" << patternName << "</a>";
+
+ // Add/remove node menu
+ if (node->Desc.Type != NT_ROOT) {
+ os << "<div class=\"dropdown pull-right\" style=\"display:inline-block\">";
+ if (node->Desc.Type == NT_PARAM) {
+ os<< "<button class=\"btn btn-xs btn-default\" type=\"button\""
+ << "\" onClick=\"window.location.href='"
+ << MakeUrlEraseSub(e, "classify", node->Parent->GetPath() + "@"
+ + ToString(node->Desc.Rollbacks) + "." + node->Desc.ParamName)
+ << "';\">"
+ "<span class=\"glyphicon glyphicon-minus\"></span>"
+ "</button>";
+ }
+ if (node->Classifier->GetChildType() != NT_PARAM) {
+ os << "<button class=\"btn btn-xs btn-default dropdown-toggle\" type=\"button\""
+ " data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"true\">"
+ "<span class=\"glyphicon glyphicon-plus\"></span>"
+ "</button>"
+ "<ul class=\"dropdown-menu\">"
+ "<li class=\"dropdown-header\">Classify by param:</li>";
+ int rollbacks = 0;
+ TPatternNode* probeNode = node;
+ while (probeNode = RollbackFind(probeNode)) {
+ const NLWTrace::TProbe* probe = probeNode->Desc.Probe;
+ os << "<li class=\"dropdown-header\">" << GetProbeName(probe) << "</li>";
+ const NLWTrace::TSignature* sgn = &probe->Event.Signature;
+ for (size_t pi = 0; pi < sgn->ParamCount; pi++) {
TString param = sgn->ParamNames[pi];
if (TrackIds.contains(param) || IsFiltered(param)) {
- continue;
- }
- os << "<li><a href=\""
- << MakeUrlAddSub(e, "classify", node->GetPath() + "@" + ToString(rollbacks) + "." + param)
- << "\">" << param << "</a></li>";
- }
- rollbacks++;
- probeNode = probeNode->Parent;
- }
- os << "</ul>";
- }
- os << "</div>";
- }
- }
-
+ continue;
+ }
+ os << "<li><a href=\""
+ << MakeUrlAddSub(e, "classify", node->GetPath() + "@" + ToString(rollbacks) + "." + param)
+ << "\">" << param << "</a></li>";
+ }
+ rollbacks++;
+ probeNode = probeNode->Parent;
+ }
+ os << "</ul>";
+ }
+ os << "</div>";
+ }
+ }
+
void OutputShare(IOutputStream& os, double share)
- {
- double lshare = share;
- double rshare = 100 - lshare;
- os << "<div class=\"progress\" style=\"margin-bottom:0px;position:relative\">"
- "<div class=\"progress-bar progress-bar-success\" role=\"progressbar\""
- " aria-valuenow=\"" << lshare << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width: " << lshare << "%;\">"
- "</div>"
- "<div class=\"progress-bar progress-bar-danger\" role=\"progressbar\""
- " aria-valuenow=\"" << rshare << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width: " << rshare << "%;\">"
- "</div>"
- "<span style=\"position:absolute;left:0;width:100%;text-align:center;z-index:2;color:white\">"
- << (share == 100? "100%": Sprintf("%2.1lf%%", share)) <<
- "</span>"
- "</div>";
- }
-
+ {
+ double lshare = share;
+ double rshare = 100 - lshare;
+ os << "<div class=\"progress\" style=\"margin-bottom:0px;position:relative\">"
+ "<div class=\"progress-bar progress-bar-success\" role=\"progressbar\""
+ " aria-valuenow=\"" << lshare << "\""
+ " aria-valuemin=\"0\""
+ " aria-valuemax=\"100\""
+ " style=\"width: " << lshare << "%;\">"
+ "</div>"
+ "<div class=\"progress-bar progress-bar-danger\" role=\"progressbar\""
+ " aria-valuenow=\"" << rshare << "\""
+ " aria-valuemin=\"0\""
+ " aria-valuemax=\"100\""
+ " style=\"width: " << rshare << "%;\">"
+ "</div>"
+ "<span style=\"position:absolute;left:0;width:100%;text-align:center;z-index:2;color:white\">"
+ << (share == 100? "100%": Sprintf("%2.1lf%%", share)) <<
+ "</span>"
+ "</div>";
+ }
+
using TTimeline = TVector<std::pair<TPatternNode*, double>>;
-
- TTimeline MakeTimeline(TPatternNode* node)
- {
- TTimeline ret;
- if (node->TrackCount == 0) {
- return ret;
- }
- ret.reserve(node->TimelineSum.size());
- for (double time : node->TimelineSum) {
- ret.emplace_back(nullptr, double(time) / node->TrackCount);
- }
- TPatternNode* n = node;
- for (auto i = ret.rbegin(), e = ret.rend(); i != e; ++i) {
+
+ TTimeline MakeTimeline(TPatternNode* node)
+ {
+ TTimeline ret;
+ if (node->TrackCount == 0) {
+ return ret;
+ }
+ ret.reserve(node->TimelineSum.size());
+ for (double time : node->TimelineSum) {
+ ret.emplace_back(nullptr, double(time) / node->TrackCount);
+ }
+ TPatternNode* n = node;
+ for (auto i = ret.rbegin(), e = ret.rend(); i != e; ++i) {
WWW_CHECK(n, "internal bug: wrong timeline length at pattern node '%s'", node->GetPath().data());
- i->first = n;
- n = n->Parent;
- }
- return ret;
- }
-
+ i->first = n;
+ n = n->Parent;
+ }
+ return ret;
+ }
+
void OutputTimeline(IOutputStream& os, const TTimeline& timeline, double maxTime)
- {
- static const char *barClass[] = {
- "progress-bar-info",
- "progress-bar-warning"
- };
- if (timeline.empty()) {
- return;
- }
- os << "<div class=\"progress\" style=\"margin-bottom:0px;color:black\">";
- double prevPos = 0.0;
- double prevTime = 0.0;
- size_t i = 0;
- for (auto& e : timeline) {
- TPatternNode* node = e.first;
- double time = e.second;
- double pos = time * 100 / maxTime;
- if (pos > 100) {
- pos = 100;
- }
- double width = pos - prevPos;
- os << "<div class=\"progress-bar " << barClass[i % 2] << "\" role=\"progressbar\""
- " aria-valuenow=\"" << width << "\""
- " aria-valuemin=\"0\""
- " aria-valuemax=\"100\""
- " style=\"width:" << width << "%;color:black\""
- " title=\"" << FormatTimelineTooltip(time, prevTime, node) << "\">";
- if (width > 20) { // To ensure text will fit the bar
- os << FormatCycles(time - prevTime);
- }
- os << "</div>";
- prevPos = pos;
- prevTime = time;
- i++;
- }
- os << "</div>";
- }
-
+ {
+ static const char *barClass[] = {
+ "progress-bar-info",
+ "progress-bar-warning"
+ };
+ if (timeline.empty()) {
+ return;
+ }
+ os << "<div class=\"progress\" style=\"margin-bottom:0px;color:black\">";
+ double prevPos = 0.0;
+ double prevTime = 0.0;
+ size_t i = 0;
+ for (auto& e : timeline) {
+ TPatternNode* node = e.first;
+ double time = e.second;
+ double pos = time * 100 / maxTime;
+ if (pos > 100) {
+ pos = 100;
+ }
+ double width = pos - prevPos;
+ os << "<div class=\"progress-bar " << barClass[i % 2] << "\" role=\"progressbar\""
+ " aria-valuenow=\"" << width << "\""
+ " aria-valuemin=\"0\""
+ " aria-valuemax=\"100\""
+ " style=\"width:" << width << "%;color:black\""
+ " title=\"" << FormatTimelineTooltip(time, prevTime, node) << "\">";
+ if (width > 20) { // To ensure text will fit the bar
+ os << FormatCycles(time - prevTime);
+ }
+ os << "</div>";
+ prevPos = pos;
+ prevTime = time;
+ i++;
+ }
+ os << "</div>";
+ }
+
TString FormatTimelineTooltip(double time, double prevTime, TPatternNode* node)
- {
- return FormatCycles(time - prevTime) + ": "
- + FormatCycles(prevTime) + " -> " + FormatCycles(time)
- + "(" + node->Name + ")";
- }
-
+ {
+ return FormatCycles(time - prevTime) + ": "
+ + FormatCycles(prevTime) + " -> " + FormatCycles(time)
+ + "(" + node->Name + ")";
+ }
+
TString FormatFloat(double value)
- {
- if (value == 0.0) {
- return "0";
- }
- if (value > 1.0) {
- if (value > 100.0) {
- return Sprintf("%.0lf", value);
- }
- if (value > 10.0) {
- return Sprintf("%.1lf", value);
- }
- return Sprintf("%.2lf", value);
- } else if (value > 1e-3) {
- if (value > 1e-1) {
- return Sprintf("%.3lf", value);
- }
- if (value > 1e-2) {
- return Sprintf("%.4lf", value);
- }
- return Sprintf("%.5lf", value);
- } else if (value > 1e-6) {
- if (value > 1e-4) {
- return Sprintf("%.6lf", value);
- }
- if (value > 1e-5) {
- return Sprintf("%.7lf", value);
- }
- return Sprintf("%.8lfus", value);
- } else {
- if (value > 1e-7) {
- return Sprintf("%.9lfns", value);
- }
- if (value > 1e-8) {
- return Sprintf("%.10lfns", value);
- }
- return Sprintf("%.2le", value);
- }
- }
-
+ {
+ if (value == 0.0) {
+ return "0";
+ }
+ if (value > 1.0) {
+ if (value > 100.0) {
+ return Sprintf("%.0lf", value);
+ }
+ if (value > 10.0) {
+ return Sprintf("%.1lf", value);
+ }
+ return Sprintf("%.2lf", value);
+ } else if (value > 1e-3) {
+ if (value > 1e-1) {
+ return Sprintf("%.3lf", value);
+ }
+ if (value > 1e-2) {
+ return Sprintf("%.4lf", value);
+ }
+ return Sprintf("%.5lf", value);
+ } else if (value > 1e-6) {
+ if (value > 1e-4) {
+ return Sprintf("%.6lf", value);
+ }
+ if (value > 1e-5) {
+ return Sprintf("%.7lf", value);
+ }
+ return Sprintf("%.8lfus", value);
+ } else {
+ if (value > 1e-7) {
+ return Sprintf("%.9lfns", value);
+ }
+ if (value > 1e-8) {
+ return Sprintf("%.10lfns", value);
+ }
+ return Sprintf("%.2le", value);
+ }
+ }
+
TString FormatCycles(double timeCycles)
- {
- double timeSec = timeCycles / NHPTimer::GetClockRate();
- if (timeSec > 1.0) {
- if (timeSec > 100.0) {
- return Sprintf("%.0lfs", timeSec);
- }
- if (timeSec > 10.0) {
- return Sprintf("%.1lfs", timeSec);
- }
- return Sprintf("%.2lfs", timeSec);
- } else if (timeSec > 1e-3) {
- if (timeSec > 1e-1) {
- return Sprintf("%.0lfms", timeSec * 1e3);
- }
- if (timeSec > 1e-2) {
- return Sprintf("%.1lfms", timeSec * 1e3);
- }
- return Sprintf("%.2lfms", timeSec * 1e3);
- } else if (timeSec > 1e-6) {
- if (timeSec > 1e-4) {
- return Sprintf("%.0lfus", timeSec * 1e6);
- }
- if (timeSec > 1e-5) {
- return Sprintf("%.1lfus", timeSec * 1e6);
- }
- return Sprintf("%.2lfus", timeSec * 1e6);
- } else {
- if (timeSec > 1e-7) {
- return Sprintf("%.0lfns", timeSec * 1e9);
- }
- if (timeSec > 1e-8) {
- return Sprintf("%.1lfns", timeSec * 1e9);
- }
- return Sprintf("%.2lfns", timeSec * 1e9);
- }
- }
-
+ {
+ double timeSec = timeCycles / NHPTimer::GetClockRate();
+ if (timeSec > 1.0) {
+ if (timeSec > 100.0) {
+ return Sprintf("%.0lfs", timeSec);
+ }
+ if (timeSec > 10.0) {
+ return Sprintf("%.1lfs", timeSec);
+ }
+ return Sprintf("%.2lfs", timeSec);
+ } else if (timeSec > 1e-3) {
+ if (timeSec > 1e-1) {
+ return Sprintf("%.0lfms", timeSec * 1e3);
+ }
+ if (timeSec > 1e-2) {
+ return Sprintf("%.1lfms", timeSec * 1e3);
+ }
+ return Sprintf("%.2lfms", timeSec * 1e3);
+ } else if (timeSec > 1e-6) {
+ if (timeSec > 1e-4) {
+ return Sprintf("%.0lfus", timeSec * 1e6);
+ }
+ if (timeSec > 1e-5) {
+ return Sprintf("%.1lfus", timeSec * 1e6);
+ }
+ return Sprintf("%.2lfus", timeSec * 1e6);
+ } else {
+ if (timeSec > 1e-7) {
+ return Sprintf("%.0lfns", timeSec * 1e9);
+ }
+ if (timeSec > 1e-8) {
+ return Sprintf("%.1lfns", timeSec * 1e9);
+ }
+ return Sprintf("%.2lfns", timeSec * 1e9);
+ }
+ }
+
TString GetParam(const NLWTrace::TLogItem& item, TString* paramValues, const TString& paramName)
- {
- for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
- if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
- return paramValues[pi];
- }
- }
+ {
+ for (size_t pi = 0; pi < item.SavedParamsCount; pi++) {
+ if (paramName == item.Probe->Event.Signature.ParamNames[pi]) {
+ return paramValues[pi];
+ }
+ }
return TString();
- }
-
+ }
+
TString GetGroup(const NLWTrace::TLogItem& item, TString* paramValues)
- {
- TStringStream ss;
- bool first = true;
+ {
+ TStringStream ss;
+ bool first = true;
for (const TString& groupParam : GroupBy) {
- ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
- first = false;
- }
- return ss.Str();
- }
-
- void AddItemToTrack(TThread::TId tid, const NLWTrace::TLogItem& item)
- {
- // Ensure cyclic per thread lwtrace logs wont drop *inner* items of a track
- // (note that some *starting* items can be dropped)
- if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
+ ss << (first? "": "|") << GetParam(item, paramValues, groupParam);
+ first = false;
+ }
+ return ss.Str();
+ }
+
+ void AddItemToTrack(TThread::TId tid, const NLWTrace::TLogItem& item)
+ {
+ // Ensure cyclic per thread lwtrace logs wont drop *inner* items of a track
+ // (note that some *starting* items can be dropped)
+ if (item.SavedParamsCount > 0 && !CutTs.Skip(item)) {
TString paramValues[LWTRACE_MAX_PARAMS];
- item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
- Tracks[GetGroup(item, paramValues)].Items.emplace_back(tid, item);
- }
- }
-};
-
-NLWTrace::TProbeRegistry g_Probes;
+ item.Probe->Event.Signature.SerializeParams(item.Params, paramValues);
+ Tracks[GetGroup(item, paramValues)].Items.emplace_back(tid, item);
+ }
+ }
+};
+
+NLWTrace::TProbeRegistry g_Probes;
TString g_sanitizerTest("TString g_sanitizerTest");
-NLWTrace::TManager g_SafeManager(g_Probes, false);
-NLWTrace::TManager g_UnsafeManager(g_Probes, true);
+NLWTrace::TManager g_SafeManager(g_Probes, false);
+NLWTrace::TManager g_UnsafeManager(g_Probes, true);
TDashboardRegistry g_DashboardRegistry;
-
-class TLWTraceMonPage : public NMonitoring::IMonPage {
+
+class TLWTraceMonPage : public NMonitoring::IMonPage {
private:
- NLWTrace::TManager* TraceMngr;
+ NLWTrace::TManager* TraceMngr;
TString StartTime;
- TTraceCleaner Cleaner;
- TMutex SnapshotsMtx;
+ TTraceCleaner Cleaner;
+ TMutex SnapshotsMtx;
THashMap<TString, TAtomicSharedPtr<NLWTrace::TLogPb>> Snapshots;
public:
- explicit TLWTraceMonPage(bool allowUnsafe = false)
+ explicit TLWTraceMonPage(bool allowUnsafe = false)
: NMonitoring::IMonPage("trace", "Tracing")
- , TraceMngr(&TraceManager(allowUnsafe))
- , Cleaner(TraceMngr)
- {
- time_t stime = TInstant::Now().TimeT();
- StartTime = CTimeR(&stime);
- }
+ , TraceMngr(&TraceManager(allowUnsafe))
+ , Cleaner(TraceMngr)
+ {
+ time_t stime = TInstant::Now().TimeT();
+ StartTime = CTimeR(&stime);
+ }
virtual void Output(NMonitoring::IMonHttpRequest& request) {
- TStringStream out;
+ TStringStream out;
try {
- if (request.GetParams().Get("mode") == "") {
- OutputTracesAndSnapshots(request, out);
+ if (request.GetParams().Get("mode") == "") {
+ OutputTracesAndSnapshots(request, out);
} else if (request.GetParams().Get("mode") == "probes") {
- OutputProbes(request, out);
+ OutputProbes(request, out);
} else if (request.GetParams().Get("mode") == "dashboards") {
OutputDashboards(request, out);
} else if (request.GetParams().Get("mode") == "dashboard") {
OutputDashboard(request, out);
} else if (request.GetParams().Get("mode") == "log") {
- OutputLog(request, out);
+ OutputLog(request, out);
} else if (request.GetParams().Get("mode") == "query") {
- OutputQuery(request, out);
- } else if (request.GetParams().Get("mode") == "builder") {
- OutputBuilder(request, out);
- } else if (request.GetParams().Get("mode") == "analytics") {
- OutputAnalytics(request, out);
+ OutputQuery(request, out);
+ } else if (request.GetParams().Get("mode") == "builder") {
+ OutputBuilder(request, out);
+ } else if (request.GetParams().Get("mode") == "analytics") {
+ OutputAnalytics(request, out);
} else if (request.GetParams().Get("mode") == "new") {
- PostNew(request, out);
+ PostNew(request, out);
} else if (request.GetParams().Get("mode") == "delete") {
- PostDelete(request, out);
- } else if (request.GetParams().Get("mode") == "make_snapshot") {
- PostSnapshot(request, out);
- } else if (request.GetParams().Get("mode") == "settimeout") {
- PostSetTimeout(request, out);
+ PostDelete(request, out);
+ } else if (request.GetParams().Get("mode") == "make_snapshot") {
+ PostSnapshot(request, out);
+ } else if (request.GetParams().Get("mode") == "settimeout") {
+ PostSetTimeout(request, out);
} else {
ythrow yexception() << "Bad request";
}
- } catch (TPageGenBase& gen) {
- out.Clear();
- out << gen.what();
- } catch (...) {
- out.Clear();
- if (request.GetParams().Get("error") == "text") {
- // Text error reply is helpful for ajax requests
- out << NMonitoring::HTTPOKTEXT;
- out << CurrentExceptionMessage();
- } else {
- WWW_HTML(out) {
- out << "<h2>Error</h2><pre>"
- << CurrentExceptionMessage()
- << Endl;
- }
- }
- }
+ } catch (TPageGenBase& gen) {
+ out.Clear();
+ out << gen.what();
+ } catch (...) {
+ out.Clear();
+ if (request.GetParams().Get("error") == "text") {
+ // Text error reply is helpful for ajax requests
+ out << NMonitoring::HTTPOKTEXT;
+ out << CurrentExceptionMessage();
+ } else {
+ WWW_HTML(out) {
+ out << "<h2>Error</h2><pre>"
+ << CurrentExceptionMessage()
+ << Endl;
+ }
+ }
+ }
request.Output() << out.Str();
- }
-
-private:
+ }
+
+private:
void OutputNavbar(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
+ {
TString active = " class=\"active\"";
- out <<
- "<nav class=\"navbar navbar-default\"><div class=\"container-fluid\">"
- << NavbarHeader() <<
- "<ul class=\"nav navbar-nav\">"
- "<li" << (request.GetParams().Get("mode") == ""? active: "") << "><a href=\"?mode=\">Traces</a></li>"
- "<li" << (request.GetParams().Get("mode") == "probes"? active: "") << "><a href=\"?mode=probes\">Probes</a></li>"
+ out <<
+ "<nav class=\"navbar navbar-default\"><div class=\"container-fluid\">"
+ << NavbarHeader() <<
+ "<ul class=\"nav navbar-nav\">"
+ "<li" << (request.GetParams().Get("mode") == ""? active: "") << "><a href=\"?mode=\">Traces</a></li>"
+ "<li" << (request.GetParams().Get("mode") == "probes"? active: "") << "><a href=\"?mode=probes\">Probes</a></li>"
"<li" << (request.GetParams().Get("mode") == "dashboards"? active: "") << "><a href=\"?mode=dashboards\">Dashboard</a></li>"
- "<li" << (request.GetParams().Get("mode") == "builder"? active: "") << "><a href=\"?mode=builder\">Builder</a></li>"
- "<li" << (request.GetParams().Get("mode") == "analytics"? active: "") << "><a href=\"?mode=analytics&id=\">Analytics</a></li>"
+ "<li" << (request.GetParams().Get("mode") == "builder"? active: "") << "><a href=\"?mode=builder\">Builder</a></li>"
+ "<li" << (request.GetParams().Get("mode") == "analytics"? active: "") << "><a href=\"?mode=analytics&id=\">Analytics</a></li>"
"<li><a href=\"https://wiki.yandex-team.ru/development/poisk/arcadia/library/cpp/lwtrace/\" target=\"_blank\">Documentation</a></li>"
- "</ul>"
- "</div></nav>"
- ;
- }
-
- template <class TReader>
- void ReadSnapshots(TReader& reader) const
- {
- TGuard<TMutex> g(SnapshotsMtx);
- for (const auto& kv : Snapshots) {
- reader.Push(kv.first, kv.second);
- }
- }
-
+ "</ul>"
+ "</div></nav>"
+ ;
+ }
+
+ template <class TReader>
+ void ReadSnapshots(TReader& reader) const
+ {
+ TGuard<TMutex> g(SnapshotsMtx);
+ for (const auto& kv : Snapshots) {
+ reader.Push(kv.first, kv.second);
+ }
+ }
+
void OutputTracesAndSnapshots(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TLogSources logSources(Cleaner);
- TraceMngr->ReadTraces(logSources);
- ReadSnapshots(logSources);
-
- TStringStream ss;
- TTracesHtmlPrinter printer(ss);
- logSources.ForEach(printer);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out <<
- "<table class=\"table table-striped\">"
- "<tr><th>Start Time</th><th>Timeout</th><th>Name</th><th>Events</th><th>Threads</th><th></th><th></th><th></th><th></th><th></th></tr>"
- << ss.Str() <<
- "</table>"
- ;
- out << "<hr/><p><strong>Start time:</strong> " << StartTime;
- out << "<br/><strong>Build date:</strong> ";
- out << __DATE__ << " " << __TIME__ << "</p>" << Endl;
- }
- }
-
+ {
+ TLogSources logSources(Cleaner);
+ TraceMngr->ReadTraces(logSources);
+ ReadSnapshots(logSources);
+
+ TStringStream ss;
+ TTracesHtmlPrinter printer(ss);
+ logSources.ForEach(printer);
+ WWW_HTML(out) {
+ OutputNavbar(request, out);
+ out <<
+ "<table class=\"table table-striped\">"
+ "<tr><th>Start Time</th><th>Timeout</th><th>Name</th><th>Events</th><th>Threads</th><th></th><th></th><th></th><th></th><th></th></tr>"
+ << ss.Str() <<
+ "</table>"
+ ;
+ out << "<hr/><p><strong>Start time:</strong> " << StartTime;
+ out << "<br/><strong>Build date:</strong> ";
+ out << __DATE__ << " " << __TIME__ << "</p>" << Endl;
+ }
+ }
+
void OutputProbes(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- TStringStream ss;
- TProbesHtmlPrinter printer;
- TraceMngr->ReadProbes(printer);
- printer.Output(ss);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << ss.Str();
- }
- }
-
+ {
+ TStringStream ss;
+ TProbesHtmlPrinter printer;
+ TraceMngr->ReadProbes(printer);
+ printer.Output(ss);
+ WWW_HTML(out) {
+ OutputNavbar(request, out);
+ out << ss.Str();
+ }
+ }
+
void OutputDashboards(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
{
TStringStream ss;
@@ -3910,7 +3910,7 @@ private:
WWW_HTML(out) {
OutputNavbar(request, out);
out << ss.Str();
- }
+ }
}
void OutputDashboard(const NMonitoring::IMonHttpRequest& request, IOutputStream& out) {
@@ -3918,97 +3918,97 @@ private:
ythrow yexception() << "Cgi-parameter 'name' is not specified";
} else {
auto name = request.GetParams().Get("name");
- NLWTrace::TDashboard dash;
+ NLWTrace::TDashboard dash;
if (!g_DashboardRegistry.Get(name, dash)) {
ythrow yexception() << "Dashboard doesn't exist";
}
WWW_HTML(out) {
OutputNavbar(request, out);
- out << "<style type='text/css'>html, body { height: 100%; }</style>";
- out << "<h2>" << dash.GetName() << "</h2>";
- if (dash.GetDescription()) {
- out << "<h3>" << dash.GetDescription() << "</h3>";
- }
- int height = 85; // %
- int minHeight = 100; // px
- out << "<table height='" << height << "%' width='100%' cellpadding='4'><tbody height='100%' width='100%'>";
- ui32 rows = 0;
- auto maxRowSpan = [](const auto& row) {
- ui32 rowSpan = 1;
- for (const auto& cell : row.GetCells()) {
- rowSpan = Max(rowSpan, cell.GetRowSpan());
- }
- return rowSpan;
- };
- for (const auto& row : dash.GetRows()) {
- rows += maxRowSpan(row);
- }
- for (const auto& row : dash.GetRows()) {
- int rowSpan = maxRowSpan(row);
- out << "<tr align='left' valign='top' style='height:" << (height * rowSpan / rows) << "%; min-height:" << (minHeight * rowSpan)<< "px'>";
- for (const auto& cell : row.GetCells()) {
- TString url = cell.GetUrl();
- TString title = cell.GetTitle();
- TString text = cell.GetText();
- auto rowSpan = Max<ui64>(1, cell.GetRowSpan());
- auto colSpan = Max<ui64>(1, cell.GetColSpan());
- if (url) {
- if (title) {
- out << "<td rowspan='" << rowSpan << "' colSpan='1'><a href=" << url << ">" << title << "</a><br>";
+ out << "<style type='text/css'>html, body { height: 100%; }</style>";
+ out << "<h2>" << dash.GetName() << "</h2>";
+ if (dash.GetDescription()) {
+ out << "<h3>" << dash.GetDescription() << "</h3>";
+ }
+ int height = 85; // %
+ int minHeight = 100; // px
+ out << "<table height='" << height << "%' width='100%' cellpadding='4'><tbody height='100%' width='100%'>";
+ ui32 rows = 0;
+ auto maxRowSpan = [](const auto& row) {
+ ui32 rowSpan = 1;
+ for (const auto& cell : row.GetCells()) {
+ rowSpan = Max(rowSpan, cell.GetRowSpan());
+ }
+ return rowSpan;
+ };
+ for (const auto& row : dash.GetRows()) {
+ rows += maxRowSpan(row);
+ }
+ for (const auto& row : dash.GetRows()) {
+ int rowSpan = maxRowSpan(row);
+ out << "<tr align='left' valign='top' style='height:" << (height * rowSpan / rows) << "%; min-height:" << (minHeight * rowSpan)<< "px'>";
+ for (const auto& cell : row.GetCells()) {
+ TString url = cell.GetUrl();
+ TString title = cell.GetTitle();
+ TString text = cell.GetText();
+ auto rowSpan = Max<ui64>(1, cell.GetRowSpan());
+ auto colSpan = Max<ui64>(1, cell.GetColSpan());
+ if (url) {
+ if (title) {
+ out << "<td rowspan='" << rowSpan << "' colSpan='1'><a href=" << url << ">" << title << "</a><br>";
+ }
+ out << "<iframe scrolling='no' width='" << 100 * colSpan << "%' height='" << height << "%' style='border: 0' src=" << url << "></iframe></td>";
+ // Add fake cells to fix html table
+ for (ui32 left = 1; left < colSpan; ++left) {
+ out << "<td height='100%' rowspan='" << rowSpan << "' colSpan='1'>"
+ << "<iframe scrolling='no' width='100%' height='100%' style='border: 0' src=" << "" << "></iframe></td>";
}
- out << "<iframe scrolling='no' width='" << 100 * colSpan << "%' height='" << height << "%' style='border: 0' src=" << url << "></iframe></td>";
- // Add fake cells to fix html table
- for (ui32 left = 1; left < colSpan; ++left) {
- out << "<td height='100%' rowspan='" << rowSpan << "' colSpan='1'>"
- << "<iframe scrolling='no' width='100%' height='100%' style='border: 0' src=" << "" << "></iframe></td>";
- }
- } else {
- out << "<td style='font-size: 25px' align='left' rowspan='" << rowSpan << "' colSpan='" << colSpan << "'>" << text << "</td>";
+ } else {
+ out << "<td style='font-size: 25px' align='left' rowspan='" << rowSpan << "' colSpan='" << colSpan << "'>" << text << "</td>";
}
}
- }
+ }
out << "</tbody></table>";
}
}
}
- static double ParseDouble(const TString& s)
- {
- if (s == "inf") {
- return std::numeric_limits<double>::infinity();
- } else if (s == "-inf") {
- return -std::numeric_limits<double>::infinity();
- } else {
- return FromString<double>(s);
- }
- }
-
+ static double ParseDouble(const TString& s)
+ {
+ if (s == "inf") {
+ return std::numeric_limits<double>::infinity();
+ } else if (s == "-inf") {
+ return -std::numeric_limits<double>::infinity();
+ } else {
+ return FromString<double>(s);
+ }
+ }
+
void OutputLog(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- if (request.GetParams().NumOfValues("id") == 0) {
- ythrow yexception() << "Cgi-parameter 'id' is not specified";
- } else {
+ {
+ if (request.GetParams().NumOfValues("id") == 0) {
+ ythrow yexception() << "Cgi-parameter 'id' is not specified";
+ } else {
const TCgiParameters& e = request.GetParams();
- TStringStream ss;
- if (e.Get("format") == "json") {
- TLogJsonPrinter printer(ss);
- printer.OutputHeader();
- TString id = e.Get("id");
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- printer.OutputFooter(TraceMngr->GetTrace(id));
- out << HTTPOKJSON;
- out << ss.Str();
- } if (e.Get("format") == "json2") {
- TLogTextPrinter printer(e);
- for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- TraceMngr->ReadDepot(id, printer);
- }
- printer.OutputJson(ss);
- out << HTTPOKJSON;
- out << ss.Str();
+ TStringStream ss;
+ if (e.Get("format") == "json") {
+ TLogJsonPrinter printer(ss);
+ printer.OutputHeader();
+ TString id = e.Get("id");
+ CheckAdHocTrace(id, TDuration::Minutes(1));
+ TraceMngr->ReadLog(id, printer);
+ printer.OutputFooter(TraceMngr->GetTrace(id));
+ out << HTTPOKJSON;
+ out << ss.Str();
+ } if (e.Get("format") == "json2") {
+ TLogTextPrinter printer(e);
+ for (const TString& id : Subvalues(e, "id")) {
+ CheckAdHocTrace(id, TDuration::Minutes(1));
+ TraceMngr->ReadLog(id, printer);
+ TraceMngr->ReadDepot(id, printer);
+ }
+ printer.OutputJson(ss);
+ out << HTTPOKJSON;
+ out << ss.Str();
} else if (e.Get("format") == "analytics" && e.Get("aggr") == "tracks") {
TLogTrackExtractor logTrackExtractor(e,
Subvalues(e, "f"),
@@ -4027,8 +4027,8 @@ private:
TLogTextPrinter printer(e);
const TString& distBy = patternAnalyzer;
- double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
- double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
+ double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
+ double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
TSampleOpts opts;
opts.ShowProvider = (e.Get("show_provider") == "y");
if (e.Get("size_limit")) {
@@ -4038,101 +4038,101 @@ private:
printer.Output(ss);
out << HTTPOKTEXT;
out << ss.Str();
- } else {
+ } else {
TLogTextPrinter printer(e);
for (const TString& id : Subvalues(e, "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, printer);
- TraceMngr->ReadDepot(id, printer);
- }
- printer.Output(ss);
- out << HTTPOKTEXT;
- out << ss.Str();
- }
- }
- }
-
+ CheckAdHocTrace(id, TDuration::Minutes(1));
+ TraceMngr->ReadLog(id, printer);
+ TraceMngr->ReadDepot(id, printer);
+ }
+ printer.Output(ss);
+ out << HTTPOKTEXT;
+ out << ss.Str();
+ }
+ }
+ }
+
void OutputQuery(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- if (request.GetParams().NumOfValues("id") == 0) {
- ythrow yexception() << "Cgi-parameter 'id' is not specified";
- } else {
+ {
+ if (request.GetParams().NumOfValues("id") == 0) {
+ ythrow yexception() << "Cgi-parameter 'id' is not specified";
+ } else {
TString id = request.GetParams().Get("id");
- const NLWTrace::TQuery& query = TraceMngr->GetTrace(id)->GetQuery();
+ const NLWTrace::TQuery& query = TraceMngr->GetTrace(id)->GetQuery();
TString queryStr = query.DebugString();
- WWW_HTML(out) {
- out << "<h2>Trace Query: " << id << "</h2><pre>" << queryStr;
- }
+ WWW_HTML(out) {
+ out << "<h2>Trace Query: " << id << "</h2><pre>" << queryStr;
+ }
}
}
-
+
void OutputBuilder(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- Y_UNUSED(request);
- WWW_HTML(out) {
- OutputNavbar(request, out);
- out << "<form class=\"form-horizontal\" action=\"?mode=new&ui=y\" method=\"POST\">";
- DIV_CLASS("form-group") {
- LABEL_CLASS_FOR("col-sm-1 control-label", "inputId") { out << "Name"; }
- DIV_CLASS("col-sm-11") {
- out << "<input class=\"form-control\" id=\"inputId\" name=\"id\" placeholder=\"mytrace\">";
- }
- }
- DIV_CLASS("form-group") {
- LABEL_CLASS_FOR("col-sm-1 control-label", "textareaQuery") { out << "Query"; }
- DIV_CLASS("col-sm-11") {
- out << "<textarea class=\"form-control\" id=\"textareaQuery\" name=\"query\" rows=\"10\"></textarea>";
- }
- }
- DIV_CLASS("form-group") {
- DIV_CLASS("col-sm-offset-1 col-sm-11") {
- out << "<button type=\"submit\" class=\"btn btn-default\">Trace</button>";
- }
- }
- out << "</form>";
- }
- }
-
+ {
+ Y_UNUSED(request);
+ WWW_HTML(out) {
+ OutputNavbar(request, out);
+ out << "<form class=\"form-horizontal\" action=\"?mode=new&ui=y\" method=\"POST\">";
+ DIV_CLASS("form-group") {
+ LABEL_CLASS_FOR("col-sm-1 control-label", "inputId") { out << "Name"; }
+ DIV_CLASS("col-sm-11") {
+ out << "<input class=\"form-control\" id=\"inputId\" name=\"id\" placeholder=\"mytrace\">";
+ }
+ }
+ DIV_CLASS("form-group") {
+ LABEL_CLASS_FOR("col-sm-1 control-label", "textareaQuery") { out << "Query"; }
+ DIV_CLASS("col-sm-11") {
+ out << "<textarea class=\"form-control\" id=\"textareaQuery\" name=\"query\" rows=\"10\"></textarea>";
+ }
+ }
+ DIV_CLASS("form-group") {
+ DIV_CLASS("col-sm-offset-1 col-sm-11") {
+ out << "<button type=\"submit\" class=\"btn btn-default\">Trace</button>";
+ }
+ }
+ out << "</form>";
+ }
+ }
+
void OutputAnalytics(const NMonitoring::IMonHttpRequest& request, TStringStream& out)
- {
- using namespace NAnalytics;
- const TCgiParameters& e = request.GetParams();
-
- TLogSources logSources(Cleaner);
- TraceMngr->ReadTraces(logSources);
- ReadSnapshots(logSources);
-
- RequireMultipleSelection(out, e, "id", "Analyze ", ListTraces(logSources));
-
- THolder<TLogFilter> logFilter;
- TLogAnalyzer* logAnalyzer = nullptr;
- TLogTrackExtractor* logTracks = nullptr;
- if (request.GetParams().Get("aggr") == "tracks") {
- logFilter.Reset(logTracks = new TLogTrackExtractor(e,
- Subvalues(request.GetParams(), "f"),
- Subvalues(request.GetParams(), "g")
- ));
+ {
+ using namespace NAnalytics;
+ const TCgiParameters& e = request.GetParams();
+
+ TLogSources logSources(Cleaner);
+ TraceMngr->ReadTraces(logSources);
+ ReadSnapshots(logSources);
+
+ RequireMultipleSelection(out, e, "id", "Analyze ", ListTraces(logSources));
+
+ THolder<TLogFilter> logFilter;
+ TLogAnalyzer* logAnalyzer = nullptr;
+ TLogTrackExtractor* logTracks = nullptr;
+ if (request.GetParams().Get("aggr") == "tracks") {
+ logFilter.Reset(logTracks = new TLogTrackExtractor(e,
+ Subvalues(request.GetParams(), "f"),
+ Subvalues(request.GetParams(), "g")
+ ));
for (const TString& id : Subvalues(request.GetParams(), "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, *logTracks);
- TraceMngr->ReadDepot(id, *logTracks);
- }
- } else {
- logFilter.Reset(logAnalyzer = new TLogAnalyzer(
- Subvalues(request.GetParams(), "f"),
- Subvalues(request.GetParams(), "g"),
- request.GetParams().Get("cutts") == "y"
- ));
+ CheckAdHocTrace(id, TDuration::Minutes(1));
+ TraceMngr->ReadLog(id, *logTracks);
+ TraceMngr->ReadDepot(id, *logTracks);
+ }
+ } else {
+ logFilter.Reset(logAnalyzer = new TLogAnalyzer(
+ Subvalues(request.GetParams(), "f"),
+ Subvalues(request.GetParams(), "g"),
+ request.GetParams().Get("cutts") == "y"
+ ));
for (const TString& id : Subvalues(request.GetParams(), "id")) {
- CheckAdHocTrace(id, TDuration::Minutes(1));
- TraceMngr->ReadLog(id, *logAnalyzer);
- TraceMngr->ReadDepot(id, *logAnalyzer);
- }
- }
-
- logFilter->FilterSelectors(out, e, "f");
-
- OptionalMultipleSelection(out, e, "g", "group by", logFilter->ListParamNames());
+ CheckAdHocTrace(id, TDuration::Minutes(1));
+ TraceMngr->ReadLog(id, *logAnalyzer);
+ TraceMngr->ReadDepot(id, *logAnalyzer);
+ }
+ }
+
+ logFilter->FilterSelectors(out, e, "f");
+
+ OptionalMultipleSelection(out, e, "g", "group by", logFilter->ListParamNames());
{
auto paramNamesList = logFilter->ListParamNames();
if (e.Get("aggr") == "tracks") {
@@ -4140,7 +4140,7 @@ private:
}
OptionalSelection(out, e, "s", "order by", paramNamesList);
}
-
+
if (e.Get("s")) {
TVariants variants;
variants.emplace_back("", "asc");
@@ -4149,130 +4149,130 @@ private:
}
TString aggr = e.Get("aggr");
- TVariants variants1; // MSVS2013 doesn't understand complex initializer lists
- variants1.emplace_back("", "without aggregation");
- variants1.emplace_back("hist", "as histogram");
- variants1.emplace_back("tracks", "tracks");
- DropdownSelector<Link>(out, e, "aggr", e.Get("aggr"), "", variants1);
-
- unsigned refresh = e.Get("refresh")?
- FromString<unsigned>(e.Get("refresh")):
- 1000;
-
- if (aggr == "tracks") {
- TVariants ileVars;
- ileVars.emplace_back("0", "0");
- ileVars.emplace_back("25", "25");
- ileVars.emplace_back("50", "50");
- ileVars.emplace_back("75", "75");
- ileVars.emplace_back("", "90");
- ileVars.emplace_back("95", "95");
- ileVars.emplace_back("99", "99");
- ileVars.emplace_back("99.9", "99.9");
- ileVars.emplace_back("100", "100");
- DropdownSelector<Link>(out, e, "ile", e.Get("ile"), "and show", ileVars);
- out << "%-ile. ";
- TString patternAnalyzer;
+ TVariants variants1; // MSVS2013 doesn't understand complex initializer lists
+ variants1.emplace_back("", "without aggregation");
+ variants1.emplace_back("hist", "as histogram");
+ variants1.emplace_back("tracks", "tracks");
+ DropdownSelector<Link>(out, e, "aggr", e.Get("aggr"), "", variants1);
+
+ unsigned refresh = e.Get("refresh")?
+ FromString<unsigned>(e.Get("refresh")):
+ 1000;
+
+ if (aggr == "tracks") {
+ TVariants ileVars;
+ ileVars.emplace_back("0", "0");
+ ileVars.emplace_back("25", "25");
+ ileVars.emplace_back("50", "50");
+ ileVars.emplace_back("75", "75");
+ ileVars.emplace_back("", "90");
+ ileVars.emplace_back("95", "95");
+ ileVars.emplace_back("99", "99");
+ ileVars.emplace_back("99.9", "99.9");
+ ileVars.emplace_back("100", "100");
+ DropdownSelector<Link>(out, e, "ile", e.Get("ile"), "and show", ileVars);
+ out << "%-ile. ";
+ TString patternAnalyzer;
TString distBy;
TString distType;
- if (e.Get("pattern")) {
- TVariants analyzePatternVars;
- analyzePatternVars.emplace_back("resTotal", "distribution by total");
- analyzePatternVars.emplace_back("resLast", "distribution by last");
- analyzePatternVars.emplace_back("covMatrix", "covariance matrix");
- DropdownSelector<Link>(
- out, e, "ptrn_anlz", e.Get("ptrn_anlz"),
- "Pattern", analyzePatternVars
- );
- patternAnalyzer = e.Get("ptrn_anlz");
-
- TVariants distTypeVars;
- distTypeVars.emplace_back("", "as is");
- distTypeVars.emplace_back("-stack", "cumulative");
- DropdownSelector<Link>(out, e, "dist_type", e.Get("dist_type"), "", distTypeVars);
- distType = e.Get("dist_type");
- } else {
- out << "<i>Select pattern for more options</i>";
- }
- logTracks->Run();
-
- if (e.Get("download") == "y") {
- out.Clear();
- out <<
- "HTTP/1.1 200 Ok\r\n"
- "Content-Type: application/force-download\r\n"
- "Content-Transfer-Encoding: binary\r\n"
- "Content-Disposition: attachment; filename=\"trace_chrome.json\"\r\n"
- "\r\n"
- ;
- logTracks->OutputChromeTrace(out, e);
- return;
- }
-
- NAnalytics::TTable distData;
- bool showSample = false;
+ if (e.Get("pattern")) {
+ TVariants analyzePatternVars;
+ analyzePatternVars.emplace_back("resTotal", "distribution by total");
+ analyzePatternVars.emplace_back("resLast", "distribution by last");
+ analyzePatternVars.emplace_back("covMatrix", "covariance matrix");
+ DropdownSelector<Link>(
+ out, e, "ptrn_anlz", e.Get("ptrn_anlz"),
+ "Pattern", analyzePatternVars
+ );
+ patternAnalyzer = e.Get("ptrn_anlz");
+
+ TVariants distTypeVars;
+ distTypeVars.emplace_back("", "as is");
+ distTypeVars.emplace_back("-stack", "cumulative");
+ DropdownSelector<Link>(out, e, "dist_type", e.Get("dist_type"), "", distTypeVars);
+ distType = e.Get("dist_type");
+ } else {
+ out << "<i>Select pattern for more options</i>";
+ }
+ logTracks->Run();
+
+ if (e.Get("download") == "y") {
+ out.Clear();
+ out <<
+ "HTTP/1.1 200 Ok\r\n"
+ "Content-Type: application/force-download\r\n"
+ "Content-Transfer-Encoding: binary\r\n"
+ "Content-Disposition: attachment; filename=\"trace_chrome.json\"\r\n"
+ "\r\n"
+ ;
+ logTracks->OutputChromeTrace(out, e);
+ return;
+ }
+
+ NAnalytics::TTable distData;
+ bool showSample = false;
TLogTextPrinter printer(e);
-
- if (patternAnalyzer == "resTotal" || patternAnalyzer == "resLast") {
- distBy = patternAnalyzer;
- distData = logTracks->Distribution(distBy, "", "", e.Get("width"));
- double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
- double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
- if (!isnan(sel_x1) && !isnan(sel_x2)) {
- showSample = true;
- TSampleOpts opts;
- opts.ShowProvider = (e.Get("show_provider") == "y");
- if (e.Get("size_limit")) {
- opts.SizeLimit = FromString<size_t>(e.Get("size_limit"));
- }
+
+ if (patternAnalyzer == "resTotal" || patternAnalyzer == "resLast") {
+ distBy = patternAnalyzer;
+ distData = logTracks->Distribution(distBy, "", "", e.Get("width"));
+ double sel_x1 = e.Get("sel_x1")? ParseDouble(e.Get("sel_x1")): NAN;
+ double sel_x2 = e.Get("sel_x2")? ParseDouble(e.Get("sel_x2")): NAN;
+ if (!isnan(sel_x1) && !isnan(sel_x2)) {
+ showSample = true;
+ TSampleOpts opts;
+ opts.ShowProvider = (e.Get("show_provider") == "y");
+ if (e.Get("size_limit")) {
+ opts.SizeLimit = FromString<size_t>(e.Get("size_limit"));
+ }
logTracks->OutputSample(distBy, sel_x1, sel_x2, opts, printer);
- }
- }
-
+ }
+ }
+
TString selectors = out.Str();
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- if (distBy) {
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = null;\n"
- " var datajson = " << ToJsonFlot(distData, distBy, {"_count_sum" + distType}) << ";\n"
- " var refreshPeriod = 0;\n"
- " var xn = \"" << distBy << "\";\n"
- " var navigate = false;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- " embededMode();"
- " enableSelection();"
- "});\n"
- "</script>\n";
- }
- // Show download button
- out <<
- "<script type=\"text/javascript\">"
- "$(function() {"
- " $(\"#download-btn\").click(function(){window.location.href='"
- << MakeUrlAdd(e, "download", "y") <<
- "';});"
- " $(\"#download-btn\").removeClass(\"hidden\");"
- "});"
- "</script>\n";
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
-
- logTracks->OutputTable(out, e);
- if (distBy) {
- out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
- if (showSample) {
+ out.Clear();
+ out << NMonitoring::HTTPOKHTML;
+ out << "<!DOCTYPE html>" << Endl;
+ HTML(out) {
+ HTML_TAG() {
+ HEAD() {
+ out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
+ if (distBy) {
+ out <<
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " var dataurl = null;\n"
+ " var datajson = " << ToJsonFlot(distData, distBy, {"_count_sum" + distType}) << ";\n"
+ " var refreshPeriod = 0;\n"
+ " var xn = \"" << distBy << "\";\n"
+ " var navigate = false;\n"
+ << NResource::Find("lwtrace/mon/static/analytics.js") <<
+ " embededMode();"
+ " enableSelection();"
+ "});\n"
+ "</script>\n";
+ }
+ // Show download button
+ out <<
+ "<script type=\"text/javascript\">"
+ "$(function() {"
+ " $(\"#download-btn\").click(function(){window.location.href='"
+ << MakeUrlAdd(e, "download", "y") <<
+ "';});"
+ " $(\"#download-btn\").removeClass(\"hidden\");"
+ "});"
+ "</script>\n";
+ }
+ BODY() {
+ // Wrap selectors with navbar
+ { TSelectorsContainer sc(out);
+ out << selectors;
+ }
+
+ logTracks->OutputTable(out, e);
+ if (distBy) {
+ out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
+ if (showSample) {
static const THashSet<TString> keepParams = {
"f",
"g",
@@ -4302,134 +4302,134 @@ private:
out << "<pre>\n";
printer.Output(out);
out << "</pre>\n";
- }
- }
-
- if (patternAnalyzer == "covMatrix") {
- logTracks->OutputSliceCovarianceMatrix(out, e);
- }
- }
- }
- }
- } else {
- double width = e.Get("width")? FromString<double>(e.Get("width")): 99;
-
- NAnalytics::TTable data;
- if (aggr == "") {
- data = logAnalyzer->GetTable();
- } else if (aggr == "hist") {
- RequireSelection(out, e, "bn", "by", logFilter->ListParamNames());
- const NAnalytics::TTable& inputTable = logAnalyzer->GetTable();
+ }
+ }
+
+ if (patternAnalyzer == "covMatrix") {
+ logTracks->OutputSliceCovarianceMatrix(out, e);
+ }
+ }
+ }
+ }
+ } else {
+ double width = e.Get("width")? FromString<double>(e.Get("width")): 99;
+
+ NAnalytics::TTable data;
+ if (aggr == "") {
+ data = logAnalyzer->GetTable();
+ } else if (aggr == "hist") {
+ RequireSelection(out, e, "bn", "by", logFilter->ListParamNames());
+ const NAnalytics::TTable& inputTable = logAnalyzer->GetTable();
TString bn = e.Get("bn");
- double b1 = e.Get("b1")? FromString<double>(e.Get("b1")): MinValue(bn, inputTable);
- double b2 = e.Get("b2")? FromString<double>(e.Get("b2")): MaxValue(bn, inputTable);
- if (isfinite(b1) && isfinite(b2)) {
- WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
- double dx = e.Get("dx")? FromString<double>(e.Get("dx")): (b2-b1)/width;
- data = HistogramAll(inputTable, e.Get("bn"), b1, b2, dx);
- } else {
- // Empty table -- it's ok -- leave data table empty
- }
- }
-
+ double b1 = e.Get("b1")? FromString<double>(e.Get("b1")): MinValue(bn, inputTable);
+ double b2 = e.Get("b2")? FromString<double>(e.Get("b2")): MaxValue(bn, inputTable);
+ if (isfinite(b1) && isfinite(b2)) {
+ WWW_CHECK(b1 <= b2, "invalid xrange [%le; %le]", b1, b2);
+ double dx = e.Get("dx")? FromString<double>(e.Get("dx")): (b2-b1)/width;
+ data = HistogramAll(inputTable, e.Get("bn"), b1, b2, dx);
+ } else {
+ // Empty table -- it's ok -- leave data table empty
+ }
+ }
+
TString xn = e.Get("xn");
-
+
TString outFormat = e.Get("out");
- TVariants variants2;
- variants2.emplace_back("html", "table");
- variants2.emplace_back("flot", "chart");
- variants2.emplace_back("gantt", "gantt");
- variants2.emplace_back("text", "text");
- variants2.emplace_back("csv", "CSV");
- variants2.emplace_back("json_flot", "JSON");
-
- RequireSelection(out, e, "out", "and show", variants2);
- if (outFormat == "csv") {
+ TVariants variants2;
+ variants2.emplace_back("html", "table");
+ variants2.emplace_back("flot", "chart");
+ variants2.emplace_back("gantt", "gantt");
+ variants2.emplace_back("text", "text");
+ variants2.emplace_back("csv", "CSV");
+ variants2.emplace_back("json_flot", "JSON");
+
+ RequireSelection(out, e, "out", "and show", variants2);
+ if (outFormat == "csv") {
TString sep = e.Get("sep")? e.Get("sep"): TString("\t");
- out.Clear();
- out << NMonitoring::HTTPOKTEXT;
- out << ToCsv(data, sep, e.Get("head") != "n");
- } else if (outFormat == "html") {
+ out.Clear();
+ out << NMonitoring::HTTPOKTEXT;
+ out << ToCsv(data, sep, e.Get("head") != "n");
+ } else if (outFormat == "html") {
TString selectors = out.Str();
- out.Clear();
- WWW_HTML(out) {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << ToHtml(data);
- }
- } else if (outFormat == "json_flot") {
- SeriesSelectors(out, e, "xn", "yns", data);
- out.Clear();
- out << NMonitoring::HTTPOKJSON;
+ out.Clear();
+ WWW_HTML(out) {
+ // Wrap selectors with navbar
+ { TSelectorsContainer sc(out);
+ out << selectors;
+ }
+ out << ToHtml(data);
+ }
+ } else if (outFormat == "json_flot") {
+ SeriesSelectors(out, e, "xn", "yns", data);
+ out.Clear();
+ out << NMonitoring::HTTPOKJSON;
out << ToJsonFlot(data, xn, SplitString(e.Get("yns"), ":"));
- } else if (outFormat == "flot") {
- SeriesSelectors(out, e, "xn", "yns", data);
+ } else if (outFormat == "flot") {
+ SeriesSelectors(out, e, "xn", "yns", data);
TString selectors = out.Str();
-
+
TVector<TString> ynos = SplitString(e.Get("yns"), ":");
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = \"" << EscapeJSONString(MakeUrl(e, "out", "json_flot")) << "\";\n"
- " var refreshPeriod = " << refresh << ";\n"
- " var xn = \"" << ParseName(xn) << "\";\n"
- " var navigate = true;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- "});\n"
- "</script>\n"
- ;
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
- }
- }
- }
- } else if (outFormat == "gantt") {
- TString selectors = out.Str();
- out.Clear();
- out << NMonitoring::HTTPOKHTML;
- out << "<!DOCTYPE html>" << Endl;
- HTML(out) {
- HTML_TAG() {
- HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
- out <<
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " var dataurl = \"" << EscapeJSONString(MakeUrl(e, {{"mode", "log"}, {"format", "json2"}, {"gantt",""}})) << "\";\n"
- " var refreshPeriod = " << refresh << ";\n"
- " var xn = \"" << ParseName(xn) << "\";\n"
- " var navigate = true;\n"
- << NResource::Find("lwtrace/mon/static/analytics.js") <<
- "});\n"
- "</script>\n"
- ;
- }
- BODY() {
- // Wrap selectors with navbar
- { TSelectorsContainer sc(out);
- out << selectors;
- }
- out << NResource::Find("lwtrace/mon/static/analytics.gantt.html") << Endl;
- }
- }
- }
+ out.Clear();
+ out << NMonitoring::HTTPOKHTML;
+ out << "<!DOCTYPE html>" << Endl;
+ HTML(out) {
+ HTML_TAG() {
+ HEAD() {
+ out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
+ out <<
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " var dataurl = \"" << EscapeJSONString(MakeUrl(e, "out", "json_flot")) << "\";\n"
+ " var refreshPeriod = " << refresh << ";\n"
+ " var xn = \"" << ParseName(xn) << "\";\n"
+ " var navigate = true;\n"
+ << NResource::Find("lwtrace/mon/static/analytics.js") <<
+ "});\n"
+ "</script>\n"
+ ;
+ }
+ BODY() {
+ // Wrap selectors with navbar
+ { TSelectorsContainer sc(out);
+ out << selectors;
+ }
+ out << NResource::Find("lwtrace/mon/static/analytics.flot.html") << Endl;
+ }
+ }
+ }
+ } else if (outFormat == "gantt") {
+ TString selectors = out.Str();
+ out.Clear();
+ out << NMonitoring::HTTPOKHTML;
+ out << "<!DOCTYPE html>" << Endl;
+ HTML(out) {
+ HTML_TAG() {
+ HEAD() {
+ out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
+ out <<
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " var dataurl = \"" << EscapeJSONString(MakeUrl(e, {{"mode", "log"}, {"format", "json2"}, {"gantt",""}})) << "\";\n"
+ " var refreshPeriod = " << refresh << ";\n"
+ " var xn = \"" << ParseName(xn) << "\";\n"
+ " var navigate = true;\n"
+ << NResource::Find("lwtrace/mon/static/analytics.js") <<
+ "});\n"
+ "</script>\n"
+ ;
+ }
+ BODY() {
+ // Wrap selectors with navbar
+ { TSelectorsContainer sc(out);
+ out << selectors;
+ }
+ out << NResource::Find("lwtrace/mon/static/analytics.gantt.html") << Endl;
+ }
+ }
+ }
} else if (outFormat = "text") {
- out << " <input type='text' id='logsLimit' size='2' placeholder='Limit'>" << Endl;
- out <<
+ out << " <input type='text' id='logsLimit' size='2' placeholder='Limit'>" << Endl;
+ out <<
R"END(<script>
{
var url = new URL(window.location.href);
@@ -4468,7 +4468,7 @@ private:
HTML(out) {
HTML_TAG() {
HEAD() {
- out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
+ out << NResource::Find("lwtrace/mon/static/analytics.header.html") << Endl;
}
BODY() {
// Wrap selectors with navbar
@@ -4511,213 +4511,213 @@ private:
}
}
}
- }
- }
- }
-
+ }
+ }
+ }
+
TDuration GetGetTimeout(const NMonitoring::IMonHttpRequest& request)
- {
- return (request.GetParams().Has("timeout")?
- TDuration::Seconds(FromString<double>(request.GetParams().Get("timeout"))):
- TDuration::Max());
- }
-
+ {
+ return (request.GetParams().Has("timeout")?
+ TDuration::Seconds(FromString<double>(request.GetParams().Get("timeout"))):
+ TDuration::Max());
+ }
+
void PostNew(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
+ {
+ WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TDuration timeout = GetGetTimeout(request);
- if (!CheckAdHocTrace(id, timeout)) {
- NLWTrace::TQuery query;
+ bool ui = (request.GetParams().Get("ui") == "y");
+ TDuration timeout = GetGetTimeout(request);
+ if (!CheckAdHocTrace(id, timeout)) {
+ NLWTrace::TQuery query;
TString queryStr = request.GetPostParams().Get("query");
- if (!ui) {
- queryStr = Base64Decode(queryStr); // Needed for trace.sh (historically)
- }
- WWW_CHECK(queryStr, "Empty trace query");
- bool parsed = NProtoBuf::TextFormat::ParseFromString(queryStr, &query);
- WWW_CHECK(parsed, "Trace query text protobuf parse failed"); // TODO[serxa]: report error line/col and message
- TraceMngr->New(id, query);
- Cleaner.Postpone(id, timeout, false);
- } else {
+ if (!ui) {
+ queryStr = Base64Decode(queryStr); // Needed for trace.sh (historically)
+ }
+ WWW_CHECK(queryStr, "Empty trace query");
+ bool parsed = NProtoBuf::TextFormat::ParseFromString(queryStr, &query);
+ WWW_CHECK(parsed, "Trace query text protobuf parse failed"); // TODO[serxa]: report error line/col and message
+ TraceMngr->New(id, query);
+ Cleaner.Postpone(id, timeout, false);
+ } else {
WWW_CHECK(!request.GetPostParams().Has("query"), "trace id '%s' is reserved for ad-hoc traces", id.data());
- }
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Trace created successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
-
+ }
+ if (ui) {
+ WWW_HTML(out) {
+ out <<
+ "<div class=\"jumbotron alert-success\">"
+ "<h2>Trace created successfully</h2>"
+ "</div>"
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " setTimeout(function() {"
+ " window.location.replace('?');"
+ " }, 1000);"
+ "});\n"
+ "</script>\n";
+ }
+ } else {
+ out << HTTPOKTEXT;
+ out << "OK\n";
+ }
+ }
+
void PostDelete(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
+ {
+ WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TraceMngr->Delete(id);
- Cleaner.Forget(id);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Trace deleted successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
-
+ bool ui = (request.GetParams().Get("ui") == "y");
+ TraceMngr->Delete(id);
+ Cleaner.Forget(id);
+ if (ui) {
+ WWW_HTML(out) {
+ out <<
+ "<div class=\"jumbotron alert-success\">"
+ "<h2>Trace deleted successfully</h2>"
+ "</div>"
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " setTimeout(function() {"
+ " window.location.replace('?');"
+ " }, 1000);"
+ "});\n"
+ "</script>\n";
+ }
+ } else {
+ out << HTTPOKTEXT;
+ out << "OK\n";
+ }
+ }
+
void PostSnapshot(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
+ {
+ WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
const TString& id = request.GetPostParams().Get("id");
- bool ui = (request.GetParams().Get("ui") == "y");
- TInstant now = TInstant::Now();
-
- TGuard<TMutex> g(SnapshotsMtx);
- const NLWTrace::TSession* trace = TraceMngr->GetTrace(id);
- struct tm tm0;
+ bool ui = (request.GetParams().Get("ui") == "y");
+ TInstant now = TInstant::Now();
+
+ TGuard<TMutex> g(SnapshotsMtx);
+ const NLWTrace::TSession* trace = TraceMngr->GetTrace(id);
+ struct tm tm0;
TString sid = id + Strftime("_%Y%m%d-%H%M%S", now.GmTime(&tm0));
- TAtomicSharedPtr<NLWTrace::TLogPb>& pbPtr = Snapshots[sid];
- pbPtr.Reset(new NLWTrace::TLogPb());
- trace->ToProtobuf(*pbPtr);
- pbPtr->SetName(sid);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Snapshot created successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
-
+ TAtomicSharedPtr<NLWTrace::TLogPb>& pbPtr = Snapshots[sid];
+ pbPtr.Reset(new NLWTrace::TLogPb());
+ trace->ToProtobuf(*pbPtr);
+ pbPtr->SetName(sid);
+ if (ui) {
+ WWW_HTML(out) {
+ out <<
+ "<div class=\"jumbotron alert-success\">"
+ "<h2>Snapshot created successfully</h2>"
+ "</div>"
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " setTimeout(function() {"
+ " window.location.replace('?');"
+ " }, 1000);"
+ "});\n"
+ "</script>\n";
+ }
+ } else {
+ out << HTTPOKTEXT;
+ out << "OK\n";
+ }
+ }
+
void PostSetTimeout(const NMonitoring::IMonHttpRequest& request, IOutputStream& out)
- {
- WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
+ {
+ WWW_CHECK(request.GetPostParams().Has("id"), "POST parameter 'id' is not specified");
const TString& id = request.GetPostParams().Get("id");
- TDuration timeout = GetGetTimeout(request);
- bool ui = (request.GetParams().Get("ui") == "y");
- Cleaner.Postpone(id, timeout, true);
- if (ui) {
- WWW_HTML(out) {
- out <<
- "<div class=\"jumbotron alert-success\">"
- "<h2>Timeout changed successfully</h2>"
- "</div>"
- "<script type=\"text/javascript\">\n"
- "$(function() {\n"
- " setTimeout(function() {"
- " window.location.replace('?');"
- " }, 1000);"
- "});\n"
- "</script>\n";
- }
- } else {
- out << HTTPOKTEXT;
- out << "OK\n";
- }
- }
+ TDuration timeout = GetGetTimeout(request);
+ bool ui = (request.GetParams().Get("ui") == "y");
+ Cleaner.Postpone(id, timeout, true);
+ if (ui) {
+ WWW_HTML(out) {
+ out <<
+ "<div class=\"jumbotron alert-success\">"
+ "<h2>Timeout changed successfully</h2>"
+ "</div>"
+ "<script type=\"text/javascript\">\n"
+ "$(function() {\n"
+ " setTimeout(function() {"
+ " window.location.replace('?');"
+ " }, 1000);"
+ "});\n"
+ "</script>\n";
+ }
+ } else {
+ out << HTTPOKTEXT;
+ out << "OK\n";
+ }
+ }
void RegisterDashboard(const TString& dashConfig) {
g_DashboardRegistry.Register(dashConfig);
}
-private:
- // Returns true iff trace is ad-hoc and ensures trace is created
+private:
+ // Returns true iff trace is ad-hoc and ensures trace is created
bool CheckAdHocTrace(const TString& id, TDuration timeout)
- {
- TAdHocTraceConfig cfg;
- if (cfg.ParseId(id)) {
- if (!TraceMngr->HasTrace(id)) {
- TraceMngr->New(id, cfg.Query());
- }
- Cleaner.Postpone(id, timeout, false);
- return true;
- }
- return false;
- }
+ {
+ TAdHocTraceConfig cfg;
+ if (cfg.ParseId(id)) {
+ if (!TraceMngr->HasTrace(id)) {
+ TraceMngr->New(id, cfg.Query());
+ }
+ Cleaner.Postpone(id, timeout, false);
+ return true;
+ }
+ return false;
+ }
};
-void RegisterPages(NMonitoring::TMonService2* mon, bool allowUnsafe) {
+void RegisterPages(NMonitoring::TMonService2* mon, bool allowUnsafe) {
THolder<NLwTraceMonPage::TLWTraceMonPage> p = MakeHolder<NLwTraceMonPage::TLWTraceMonPage>(allowUnsafe);
- mon->Register(p.Release());
-
-#define WWW_STATIC_FILE(file, type) \
- mon->Register(new TResourceMonPage(file, file, NMonitoring::TResourceMonPage::type));
- WWW_STATIC_FILE("lwtrace/mon/static/common.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/common.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/css/bootstrap.min.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/css/d3-gantt.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/css/jquery.treegrid.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/analytics.css", CSS);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.eot", FONT_EOT);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.svg", SVG);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.ttf", FONT_TTF);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff2", FONT_WOFF2);
- WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff", FONT_WOFF);
- WWW_STATIC_FILE("lwtrace/mon/static/img/collapse.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/expand.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/file.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/img/folder.png", PNG);
- WWW_STATIC_FILE("lwtrace/mon/static/js/bootstrap.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3.v4.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3-gantt.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/d3-tip-0.8.0-alpha.1.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/filesaver.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.extents.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.navigate.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.selection.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.bootstrap3.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.min.js", JAVASCRIPT);
- WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.url.min.js", JAVASCRIPT);
-#undef WWW_STATIC_FILE
+ mon->Register(p.Release());
+
+#define WWW_STATIC_FILE(file, type) \
+ mon->Register(new TResourceMonPage(file, file, NMonitoring::TResourceMonPage::type));
+ WWW_STATIC_FILE("lwtrace/mon/static/common.css", CSS);
+ WWW_STATIC_FILE("lwtrace/mon/static/common.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/css/bootstrap.min.css", CSS);
+ WWW_STATIC_FILE("lwtrace/mon/static/css/d3-gantt.css", CSS);
+ WWW_STATIC_FILE("lwtrace/mon/static/css/jquery.treegrid.css", CSS);
+ WWW_STATIC_FILE("lwtrace/mon/static/analytics.css", CSS);
+ WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.eot", FONT_EOT);
+ WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.svg", SVG);
+ WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.ttf", FONT_TTF);
+ WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff2", FONT_WOFF2);
+ WWW_STATIC_FILE("lwtrace/mon/static/fonts/glyphicons-halflings-regular.woff", FONT_WOFF);
+ WWW_STATIC_FILE("lwtrace/mon/static/img/collapse.png", PNG);
+ WWW_STATIC_FILE("lwtrace/mon/static/img/expand.png", PNG);
+ WWW_STATIC_FILE("lwtrace/mon/static/img/file.png", PNG);
+ WWW_STATIC_FILE("lwtrace/mon/static/img/folder.png", PNG);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/bootstrap.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/d3.v4.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/d3-gantt.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/d3-tip-0.8.0-alpha.1.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/filesaver.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.extents.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.navigate.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.flot.selection.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.bootstrap3.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.treegrid.min.js", JAVASCRIPT);
+ WWW_STATIC_FILE("lwtrace/mon/static/js/jquery.url.min.js", JAVASCRIPT);
+#undef WWW_STATIC_FILE
}
-
-NLWTrace::TProbeRegistry& ProbeRegistry() {
- return g_Probes;
-}
-
-NLWTrace::TManager& TraceManager(bool allowUnsafe) {
- return allowUnsafe? g_UnsafeManager: g_SafeManager;
-}
-
+
+NLWTrace::TProbeRegistry& ProbeRegistry() {
+ return g_Probes;
+}
+
+NLWTrace::TManager& TraceManager(bool allowUnsafe) {
+ return allowUnsafe? g_UnsafeManager: g_SafeManager;
+}
+
TDashboardRegistry& DashboardRegistry() {
return g_DashboardRegistry;
}
-} // namespace NLwTraceMonPage
+} // namespace NLwTraceMonPage