aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/dbg_output
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/dbg_output
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/dbg_output')
-rw-r--r--library/cpp/dbg_output/DONT_COMMIT.h16
-rw-r--r--library/cpp/dbg_output/auto.h22
-rw-r--r--library/cpp/dbg_output/colorscheme.h100
-rw-r--r--library/cpp/dbg_output/dump.cpp1
-rw-r--r--library/cpp/dbg_output/dump.h106
-rw-r--r--library/cpp/dbg_output/dumpers.cpp1
-rw-r--r--library/cpp/dbg_output/dumpers.h173
-rw-r--r--library/cpp/dbg_output/engine.cpp33
-rw-r--r--library/cpp/dbg_output/engine.h180
-rw-r--r--library/cpp/dbg_output/ut/dbg_output_ut.cpp106
-rw-r--r--library/cpp/dbg_output/ut/ya.make9
-rw-r--r--library/cpp/dbg_output/ya.make15
12 files changed, 762 insertions, 0 deletions
diff --git a/library/cpp/dbg_output/DONT_COMMIT.h b/library/cpp/dbg_output/DONT_COMMIT.h
new file mode 100644
index 0000000000..e7b3182c20
--- /dev/null
+++ b/library/cpp/dbg_output/DONT_COMMIT.h
@@ -0,0 +1,16 @@
+#pragma once
+
+// Including this file is possible without modifying PEERDIR (for debug purposes).
+// The latter is allowed only locally, so this file is named
+// in such a way that including it prevents from committing the #include via ARC-1205.
+
+#define DBGDUMP_INLINE_IF_INCLUDED inline
+
+#include "dump.cpp"
+#include "dumpers.cpp"
+#include "engine.cpp"
+
+#include <library/cpp/colorizer/colors.cpp>
+#include <library/cpp/colorizer/output.cpp>
+
+#undef DBGDUMP_INLINE_IF_INCLUDED
diff --git a/library/cpp/dbg_output/auto.h b/library/cpp/dbg_output/auto.h
new file mode 100644
index 0000000000..8d96167f6a
--- /dev/null
+++ b/library/cpp/dbg_output/auto.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <util/generic/va_args.h>
+
+// int a = 1, b = 2; Cout << LabeledDump(a, b, 1 + 2); yields {"a": 1, "b": 2, "1 + 2": 3}
+#define LabeledDump(...) \
+ '{' Y_PASS_VA_ARGS(Y_MAP_ARGS_WITH_LAST(__LABELED_DUMP_NONLAST__, __LABELED_DUMP_IMPL__, __VA_ARGS__)) << '}'
+#define __LABELED_DUMP_IMPL__(x) << "\"" #x "\": " << DbgDump(x)
+#define __LABELED_DUMP_NONLAST__(x) __LABELED_DUMP_IMPL__(x) << ", "
+
+// Usage: struct TMyStruct { int A, B; }; DEFINE_DUMPER(TMyStruct, A, B); Cout << TMyStruct{3, 4};
+// yields {"A": 3, "B": 4}
+#define DEFINE_DUMPER(C, ...) \
+ template <> \
+ struct TDumper<C> { \
+ template <class S> \
+ static inline void Dump(S& s, const C& v) { \
+ s << DumpRaw("{") Y_PASS_VA_ARGS(Y_MAP_ARGS_WITH_LAST(__DEFINE_DUMPER_NONLAST__, __DEFINE_DUMPER_IMPL__, __VA_ARGS__)) << DumpRaw("}"); \
+ } \
+ };
+#define __DEFINE_DUMPER_IMPL__(x) << DumpRaw("\"" #x "\": ") << v.x
+#define __DEFINE_DUMPER_NONLAST__(x) __DEFINE_DUMPER_IMPL__(x) << DumpRaw(", ")
diff --git a/library/cpp/dbg_output/colorscheme.h b/library/cpp/dbg_output/colorscheme.h
new file mode 100644
index 0000000000..a5b9cf749a
--- /dev/null
+++ b/library/cpp/dbg_output/colorscheme.h
@@ -0,0 +1,100 @@
+#pragma once
+
+#include "engine.h"
+#include <library/cpp/colorizer/output.h>
+
+#ifndef DBG_OUTPUT_DEFAULT_COLOR_SCHEME
+#define DBG_OUTPUT_DEFAULT_COLOR_SCHEME NDbgDump::NColorScheme::TPlain
+#endif
+
+#define DBG_OUTPUT_COLOR_HANDLER(NAME) \
+ template <class S> \
+ inline void NAME(S& stream)
+
+namespace NDbgDump {
+ namespace NColorScheme {
+ /// Start by copying this one if you want to define a custom color scheme.
+ struct TPlain {
+ // Foreground color modifiers
+ DBG_OUTPUT_COLOR_HANDLER(Markup) {
+ Y_UNUSED(stream);
+ }
+ DBG_OUTPUT_COLOR_HANDLER(String) {
+ Y_UNUSED(stream);
+ }
+ DBG_OUTPUT_COLOR_HANDLER(Literal) {
+ Y_UNUSED(stream);
+ }
+ DBG_OUTPUT_COLOR_HANDLER(ResetType) {
+ Y_UNUSED(stream);
+ }
+
+ // Background color modifiers
+ DBG_OUTPUT_COLOR_HANDLER(Key) {
+ Y_UNUSED(stream);
+ }
+ DBG_OUTPUT_COLOR_HANDLER(Value) {
+ Y_UNUSED(stream);
+ }
+ DBG_OUTPUT_COLOR_HANDLER(ResetRole) {
+ Y_UNUSED(stream);
+ }
+ };
+
+ /// Use this one if you want colors but are lazy enough to define a custom color scheme.
+ /// Be careful enough to use DumpRaw for avoiding an endless recursion.
+ /// Enforce controls whether colors should be applied even if stdout is not a TTY.
+ template <bool Enforce = false>
+ class TEyebleed {
+ public:
+ TEyebleed() {
+ if (Enforce) {
+ Colors.Enable();
+ }
+ }
+
+ // Foreground color modifiers
+ DBG_OUTPUT_COLOR_HANDLER(Markup) {
+ stream << DumpRaw(Colors.LightGreenColor());
+ }
+ DBG_OUTPUT_COLOR_HANDLER(String) {
+ stream << DumpRaw(Colors.YellowColor());
+ }
+ DBG_OUTPUT_COLOR_HANDLER(Literal) {
+ stream << DumpRaw(Colors.LightRedColor());
+ }
+ DBG_OUTPUT_COLOR_HANDLER(ResetType) {
+ stream << DumpRaw(Colors.OldColor());
+ }
+
+ // Background color modifiers
+ // TODO: support backgrounds in library/cpp/colorizer
+ DBG_OUTPUT_COLOR_HANDLER(Key) {
+ if (Depth++ == 0 && Colors.IsTTY()) {
+ stream << DumpRaw(TStringBuf("\033[42m"));
+ }
+ }
+ DBG_OUTPUT_COLOR_HANDLER(Value) {
+ if (Depth++ == 0 && Colors.IsTTY()) {
+ stream << DumpRaw(TStringBuf("\033[44m"));
+ }
+ }
+ DBG_OUTPUT_COLOR_HANDLER(ResetRole) {
+ if (--Depth == 0 && Colors.IsTTY()) {
+ stream << DumpRaw(TStringBuf("\033[49m"));
+ }
+ }
+
+ private:
+ NColorizer::TColors Colors;
+ size_t Depth = 0;
+ };
+ }
+}
+
+namespace NPrivate {
+ template <typename CS>
+ struct TColorSchemeContainer {
+ CS ColorScheme;
+ };
+}
diff --git a/library/cpp/dbg_output/dump.cpp b/library/cpp/dbg_output/dump.cpp
new file mode 100644
index 0000000000..ab09a93822
--- /dev/null
+++ b/library/cpp/dbg_output/dump.cpp
@@ -0,0 +1 @@
+#include "dump.h"
diff --git a/library/cpp/dbg_output/dump.h b/library/cpp/dbg_output/dump.h
new file mode 100644
index 0000000000..c7efa105ee
--- /dev/null
+++ b/library/cpp/dbg_output/dump.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include "engine.h"
+#include "dumpers.h"
+#include "auto.h"
+#include "colorscheme.h"
+
+#include <util/stream/format.h>
+#include <util/system/type_name.h>
+#include <util/generic/hash_set.h>
+#include <utility>
+
+/*
+ * Cout << DbgDump(any) << Endl;
+ * Cout << DbgDumpDeep(any) << Endl;
+ * Cout << DbgDump(any).SetIndent(true) << Endl;
+ *
+ * specialize TDumper<your type> for extending dumper
+ */
+
+namespace NPrivate {
+ template <class TColorScheme>
+ struct TTraitsShallow {
+ struct TDump: public TDumpBase, public TColorSchemeContainer<TColorScheme> {
+ template <typename... Args>
+ inline TDump(Args&&... args)
+ : TDumpBase(std::forward<Args>(args)...)
+ {
+ }
+
+ template <class V>
+ inline void Pointer(const V* v) {
+ if (v) {
+ *this << DumpRaw("(") << DumpRaw(TypeName(v).data()) << DumpRaw(")") << Hex((size_t)v);
+ } else {
+ *this << DumpRaw("(") << DumpRaw(TypeName<V>().data()) << DumpRaw("*)nullptr");
+ }
+ }
+ };
+ };
+
+ template <class TColorScheme>
+ struct TTraitsDeep {
+ struct TDump: public TDumpBase, public TColorSchemeContainer<TColorScheme> {
+ template <typename... Args>
+ inline TDump(Args&&... args)
+ : TDumpBase(std::forward<Args>(args)...)
+ {
+ }
+
+ template <class V>
+ inline void Pointer(const V* v) {
+ if (v && !Visited.contains((size_t)v)) {
+ Visited.insert((size_t)v);
+ *this << DumpRaw("(") << DumpRaw(TypeName(v).data()) << DumpRaw(")") << Hex((size_t)v) << DumpRaw(" -> ") << *v;
+ Visited.erase((size_t)v);
+ } else {
+ *this << DumpRaw("(") << DumpRaw(TypeName<V>().data()) << DumpRaw("*)nullptr");
+ }
+ }
+
+ THashSet<size_t> Visited;
+ };
+ };
+
+ template <class T, class TTraits>
+ struct TDbgDump {
+ inline TDbgDump(const T* t)
+ : T_(t)
+ , Indent(false)
+ {
+ }
+
+ inline void DumpTo(IOutputStream& out) const {
+ typename TTraits::TDump d(out, Indent);
+
+ d << *T_;
+ }
+
+ inline TDbgDump& SetIndent(bool v) noexcept {
+ Indent = v;
+
+ return *this;
+ }
+
+ const T* T_;
+ bool Indent;
+ };
+
+ template <class T, class TTraits>
+ static inline IOutputStream& operator<<(IOutputStream& out, const TDbgDump<T, TTraits>& d) {
+ d.DumpTo(out);
+
+ return out;
+ }
+}
+
+template <class T, class TColorScheme = DBG_OUTPUT_DEFAULT_COLOR_SCHEME>
+static inline ::NPrivate::TDbgDump<T, ::NPrivate::TTraitsShallow<TColorScheme>> DbgDump(const T& t) {
+ return {std::addressof(t)};
+}
+
+template <class T, class TColorScheme = DBG_OUTPUT_DEFAULT_COLOR_SCHEME>
+static inline ::NPrivate::TDbgDump<T, ::NPrivate::TTraitsDeep<TColorScheme>> DbgDumpDeep(const T& t) {
+ return {std::addressof(t)};
+}
diff --git a/library/cpp/dbg_output/dumpers.cpp b/library/cpp/dbg_output/dumpers.cpp
new file mode 100644
index 0000000000..b85d15755b
--- /dev/null
+++ b/library/cpp/dbg_output/dumpers.cpp
@@ -0,0 +1 @@
+#include "dumpers.h"
diff --git a/library/cpp/dbg_output/dumpers.h b/library/cpp/dbg_output/dumpers.h
new file mode 100644
index 0000000000..4868e97da0
--- /dev/null
+++ b/library/cpp/dbg_output/dumpers.h
@@ -0,0 +1,173 @@
+#pragma once
+
+#include "engine.h"
+
+#include <util/generic/fwd.h>
+#include <util/generic/strbuf.h>
+#include <util/generic/string.h>
+
+//smart pointers
+template <class T, class D>
+struct TDumper<TAutoPtr<T, D>> {
+ template <class S>
+ static inline void Dump(S& s, const TAutoPtr<T, D>& v) {
+ s << DumpRaw("TAutoPtr(") << v.Get() << DumpRaw(")");
+ }
+};
+
+template <class T, class D>
+struct TDumper<THolder<T, D>> {
+ template <class S>
+ static inline void Dump(S& s, const THolder<T, D>& v) {
+ s << DumpRaw("THolder(") << v.Get() << DumpRaw(")");
+ }
+};
+
+template <class T, class Ops>
+struct TDumper<TIntrusivePtr<T, Ops>> {
+ template <class S>
+ static inline void Dump(S& s, const TIntrusivePtr<T, Ops>& v) {
+ s << DumpRaw("TIntrusivePtr(") << v.Get() << DumpRaw(")");
+ }
+};
+
+template <class T, class C, class D>
+struct TDumper<TSharedPtr<T, C, D>> {
+ template <class S>
+ static inline void Dump(S& s, const TSharedPtr<T, C, D>& v) {
+ s << DumpRaw("TSharedPtr(") << v.Get() << DumpRaw(")");
+ }
+};
+
+template <class T, class C, class D>
+struct TDumper<TCopyPtr<T, C, D>> {
+ template <class S>
+ static inline void Dump(S& s, const TCopyPtr<T, C, D>& v) {
+ s << DumpRaw("TCopyPtr(") << v.Get() << DumpRaw(")");
+ }
+};
+
+//small ints
+// Default dumper prints them via IOutputStream << (value), which results in raw
+// chars, not integer values. Cast to a bigger int type to force printing as
+// integers.
+// NB: i8 = signed char != char != unsigned char = ui8
+template <>
+struct TDumper<ui8>: public TDumper<i32> {
+};
+
+template <>
+struct TDumper<i8>: public TDumper<i32> {
+};
+
+//chars
+template <>
+struct TDumper<char>: public TCharDumper {
+};
+
+template <>
+struct TDumper<wchar16>: public TCharDumper {
+};
+
+//pairs
+template <class A, class B>
+struct TDumper<std::pair<A, B>> {
+ template <class S>
+ static inline void Dump(S& s, const std::pair<A, B>& v) {
+ s.ColorScheme.Key(s);
+ s.ColorScheme.Literal(s);
+ s << v.first;
+ s.ColorScheme.ResetType(s);
+ s.ColorScheme.ResetRole(s);
+ s.ColorScheme.Markup(s);
+ s << DumpRaw(" -> ");
+ s.ColorScheme.Value(s);
+ s.ColorScheme.Literal(s);
+ s << v.second;
+ s.ColorScheme.ResetType(s);
+ s.ColorScheme.ResetRole(s);
+ }
+};
+
+//sequences
+template <class T, class A>
+struct TDumper<TVector<T, A>>: public TSeqDumper {
+};
+
+template <class T, class A>
+struct TDumper<std::vector<T, A>>: public TSeqDumper {
+};
+
+template <class T>
+struct TDumper<TArrayRef<T>>: public TSeqDumper {
+};
+
+template <class T, size_t N>
+struct TDumper<std::array<T, N>>: public TSeqDumper {
+};
+
+template <class T, class A>
+struct TDumper<TDeque<T, A>>: public TSeqDumper {
+};
+
+template <class T, class A>
+struct TDumper<TList<T, A>>: public TSeqDumper {
+};
+
+//associatives
+template <class K, class V, class P, class A>
+struct TDumper<TMap<K, V, P, A>>: public TAssocDumper {
+};
+
+template <class K, class V, class P, class A>
+struct TDumper<TMultiMap<K, V, P, A>>: public TAssocDumper {
+};
+
+template <class T, class P, class A>
+struct TDumper<TSet<T, P, A>>: public TAssocDumper {
+};
+
+template <class T, class P, class A>
+struct TDumper<TMultiSet<T, P, A>>: public TAssocDumper {
+};
+
+template <class K, class V, class H, class P, class A>
+struct TDumper<THashMap<K, V, H, P, A>>: public TAssocDumper {
+};
+
+template <class K, class V, class H, class P, class A>
+struct TDumper<THashMultiMap<K, V, H, P, A>>: public TAssocDumper {
+};
+
+template <class T, class H, class P, class A>
+struct TDumper<THashSet<T, H, P, A>>: public TAssocDumper {
+};
+
+template <class T, class H, class P, class A>
+struct TDumper<THashMultiSet<T, H, P, A>>: public TAssocDumper {
+};
+
+//strings
+template <>
+struct TDumper<TString>: public TStrDumper {
+};
+
+template <>
+struct TDumper<const char*>: public TStrDumper {
+};
+
+template <>
+struct TDumper<TUtf16String>: public TStrDumper {
+};
+
+template <>
+struct TDumper<const wchar16*>: public TStrDumper {
+};
+
+template <class C, class T, class A>
+struct TDumper<std::basic_string<C, T, A>>: public TStrDumper {
+};
+
+template <class TChar>
+struct TDumper<TBasicStringBuf<TChar>>: public TStrDumper {
+};
diff --git a/library/cpp/dbg_output/engine.cpp b/library/cpp/dbg_output/engine.cpp
new file mode 100644
index 0000000000..dcb9f02522
--- /dev/null
+++ b/library/cpp/dbg_output/engine.cpp
@@ -0,0 +1,33 @@
+#include "engine.h"
+
+#include <util/string/cast.h>
+#include <util/string/escape.h>
+
+#if !defined(DBGDUMP_INLINE_IF_INCLUDED)
+#define DBGDUMP_INLINE_IF_INCLUDED
+#endif
+
+DBGDUMP_INLINE_IF_INCLUDED void TDumpBase::String(const TStringBuf& s) {
+ if (s) {
+ Raw(TString(s).Quote());
+ } else {
+ Raw("(empty)");
+ }
+}
+
+DBGDUMP_INLINE_IF_INCLUDED void TDumpBase::String(const TWtringBuf& s) {
+ Raw("w");
+ String(ToString(s));
+}
+
+DBGDUMP_INLINE_IF_INCLUDED void TDumpBase::Raw(const TStringBuf& s) {
+ Stream().Write(s.data(), s.size());
+}
+
+DBGDUMP_INLINE_IF_INCLUDED void TDumpBase::Char(char ch) {
+ Raw("'" + EscapeC(&ch, 1) + "'");
+}
+
+DBGDUMP_INLINE_IF_INCLUDED void TDumpBase::Char(wchar16 ch) {
+ Raw("w'" + ToString(EscapeC(&ch, 1)) + "'");
+}
diff --git a/library/cpp/dbg_output/engine.h b/library/cpp/dbg_output/engine.h
new file mode 100644
index 0000000000..f13c728c39
--- /dev/null
+++ b/library/cpp/dbg_output/engine.h
@@ -0,0 +1,180 @@
+#pragma once
+
+#include <util/stream/output.h>
+
+#include <utility>
+#include <util/generic/strbuf.h>
+
+template <class T>
+struct TDumper {
+ template <class S>
+ static inline void Dump(S& s, const T& t) {
+ s.Stream() << t;
+ }
+};
+
+namespace NDumpPrivate {
+ template <class T, class V>
+ inline void Dump(T& t, const V& v) {
+ ::TDumper<V>::Dump(t, v);
+ }
+
+ template <class T, class V>
+ inline T&& operator<<(T&& t, V&& v) {
+ Dump(t, v);
+
+ return std::forward<T>(t);
+ }
+
+ struct TADLBase {
+ };
+}
+
+struct TDumpBase: public ::NDumpPrivate::TADLBase {
+ inline TDumpBase(IOutputStream& out, bool indent) noexcept
+ : Out(&out)
+ , IndentLevel(0)
+ , Indent(indent)
+ {
+ }
+
+ inline IOutputStream& Stream() const noexcept {
+ return *Out;
+ }
+
+ void Char(char ch);
+ void Char(wchar16 ch);
+
+ void String(const TStringBuf& s);
+ void String(const TWtringBuf& s);
+
+ void Raw(const TStringBuf& s);
+
+ IOutputStream* Out;
+ size_t IndentLevel;
+ bool Indent;
+};
+
+struct TIndentScope {
+ inline TIndentScope(TDumpBase& d)
+ : D(&d)
+ {
+ ++(D->IndentLevel);
+ }
+
+ inline ~TIndentScope() {
+ --(D->IndentLevel);
+ }
+
+ TDumpBase* D;
+};
+
+template <class TChar>
+struct TRawLiteral {
+ const TBasicStringBuf<TChar> S;
+};
+
+template <class TChar>
+static inline TRawLiteral<TChar> DumpRaw(const TBasicStringBuf<TChar>& s) noexcept {
+ return {s};
+}
+
+template <class TChar>
+static inline TRawLiteral<TChar> DumpRaw(const TChar* s) noexcept {
+ return {s};
+}
+
+template <class C>
+struct TDumper<TRawLiteral<C>> {
+ template <class S>
+ static inline void Dump(S& s, const TRawLiteral<C>& v) {
+ s.Raw(v.S);
+ }
+};
+
+struct TIndentNewLine {
+};
+
+static inline TIndentNewLine IndentNewLine() noexcept {
+ return {};
+}
+
+template <>
+struct TDumper<TIndentNewLine> {
+ template <class S>
+ static inline void Dump(S& s, const TIndentNewLine&) {
+ if (s.Indent) {
+ s << DumpRaw("\n") << DumpRaw(TString(s.IndentLevel * 4, ' ').data());
+ }
+ }
+};
+
+template <class P>
+struct TDumper<const P*> {
+ template <class S>
+ static inline void Dump(S& s, const P* p) {
+ s.Pointer(p);
+ }
+};
+
+template <class P>
+struct TDumper<P*>: public TDumper<const P*> {
+};
+
+struct TCharDumper {
+ template <class S, class V>
+ static inline void Dump(S& s, const V& v) {
+ s.Char(v);
+ }
+};
+
+template <class S, class V>
+static inline void OutSequence(S& s, const V& v, const char* openTag, const char* closeTag) {
+ s.ColorScheme.Markup(s);
+ s << DumpRaw(openTag);
+
+ {
+ TIndentScope scope(s);
+ size_t cnt = 0;
+
+ for (const auto& x : v) {
+ if (cnt) {
+ s.ColorScheme.Markup(s);
+ s << DumpRaw(", ");
+ }
+
+ s << IndentNewLine();
+ s.ColorScheme.Literal(s);
+ s << x;
+ ++cnt;
+ }
+ }
+
+ s << IndentNewLine();
+ s.ColorScheme.Markup(s);
+ s << DumpRaw(closeTag);
+ s.ColorScheme.ResetType(s);
+}
+
+struct TAssocDumper {
+ template <class S, class V>
+ static inline void Dump(S& s, const V& v) {
+ ::OutSequence(s, v, "{", "}");
+ }
+};
+
+struct TSeqDumper {
+ template <class S, class V>
+ static inline void Dump(S& s, const V& v) {
+ ::OutSequence(s, v, "[", "]");
+ }
+};
+
+struct TStrDumper {
+ template <class S, class V>
+ static inline void Dump(S& s, const V& v) {
+ s.ColorScheme.String(s);
+ s.String(v);
+ s.ColorScheme.ResetType(s);
+ }
+};
diff --git a/library/cpp/dbg_output/ut/dbg_output_ut.cpp b/library/cpp/dbg_output/ut/dbg_output_ut.cpp
new file mode 100644
index 0000000000..7b285c84cb
--- /dev/null
+++ b/library/cpp/dbg_output/ut/dbg_output_ut.cpp
@@ -0,0 +1,106 @@
+#include <library/cpp/dbg_output/dump.h>
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <util/stream/str.h>
+#include <util/string/builder.h>
+#include <util/string/escape.h>
+#include <util/generic/map.h>
+
+namespace {
+ struct TX {
+ inline TX() {
+ N = this;
+ }
+
+ TX* N;
+ };
+}
+
+template <>
+struct TDumper<TX> {
+ template <class S>
+ static inline void Dump(S& s, const TX& x) {
+ s << DumpRaw("x") << x.N;
+ }
+};
+
+namespace TMyNS {
+ struct TMyStruct {
+ int A, B;
+ };
+}
+DEFINE_DUMPER(TMyNS::TMyStruct, A, B)
+
+Y_UNIT_TEST_SUITE(TContainerPrintersTest) {
+ Y_UNIT_TEST(TestVectorInt) {
+ TStringStream out;
+ out << DbgDump(TVector<int>({1, 2, 3, 4, 5}));
+ UNIT_ASSERT_STRINGS_EQUAL(out.Str(), "[1, 2, 3, 4, 5]");
+ }
+
+ Y_UNIT_TEST(TestMapCharToCharArray) {
+ TStringStream out;
+
+ TMap<char, const char*> m;
+
+ m['a'] = "SMALL LETTER A";
+ m['b'] = nullptr;
+
+ out << DbgDump(m);
+
+ UNIT_ASSERT_STRINGS_EQUAL(out.Str(), "{'a' -> \"SMALL LETTER A\", 'b' -> (empty)}");
+ }
+
+ Y_UNIT_TEST(TestVectorOfVectors) {
+ TStringStream out;
+ TVector<TVector<wchar16>> vec(2);
+ vec[0].push_back(0);
+ vec[1] = {wchar16('a')};
+ out << DbgDump(vec);
+ UNIT_ASSERT_STRINGS_EQUAL(out.Str(), "[[w'\\0'], [w'a']]");
+ }
+
+ Y_UNIT_TEST(TestInfinite) {
+ UNIT_ASSERT(!!(TStringBuilder() << DbgDumpDeep(TX())));
+ }
+
+ Y_UNIT_TEST(TestLabeledDump) {
+ TStringStream out;
+ int a = 1, b = 2;
+ out << LabeledDump(a, b, 1 + 2);
+ UNIT_ASSERT_STRINGS_EQUAL(out.Str(), "{\"a\": 1, \"b\": 2, \"1 + 2\": 3}");
+ }
+
+ Y_UNIT_TEST(TestStructDumper) {
+ TStringStream out;
+ out << DbgDump(TMyNS::TMyStruct{3, 4});
+ UNIT_ASSERT_STRINGS_EQUAL(out.Str(), "{\"A\": 3, \"B\": 4}");
+ }
+
+ Y_UNIT_TEST(TestColors) {
+ using TComplex = TMap<TString, TMap<int, char>>;
+ TComplex test;
+ test["a"][1] = '7';
+ test["b"][2] = '6';
+ TStringStream out;
+ out << DbgDump<TComplex, NDbgDump::NColorScheme::TEyebleed</* Enforce = */ true>>(test);
+ UNIT_ASSERT_STRINGS_EQUAL(
+ EscapeC(out.Str()),
+ "\\x1B[1;32m{\\x1B[1;31m\\x1B[42m\\x1B[1;31m\\x1B[1;33m\\\"a\\\"\\x1B[22;39m\\x1B[22;39m"
+ "\\x1B[49m\\x1B[1;32m -> \\x1B[44m\\x1B[1;31m\\x1B[1;32m{\\x1B[1;31m\\x1B[1;31m1"
+ "\\x1B[22;39m\\x1B[1;32m -> \\x1B[1;31m'7'\\x1B[22;39m\\x1B[1;32m}"
+ "\\x1B[22;39m\\x1B[22;39m\\x1B[49m\\x1B[1;32m, \\x1B[1;31m\\x1B[42m\\x1B[1;31m\\x1B[1;33m"
+ "\\\"b\\\"\\x1B[22;39m\\x1B[22;39m\\x1B[49m\\x1B[1;32m -> "
+ "\\x1B[44m\\x1B[1;31m\\x1B[1;32m{\\x1B[1;31m\\x1B[1;31m2\\x1B[22;39m\\x1B[1;32m -> "
+ "\\x1B[1;31m'6'\\x1B[22;39m\\x1B[1;32m}\\x1B[22;39m\\x1B[22;39m\\x1B[49m\\x1B[1;32m}\\x1B[22;39m");
+ }
+
+ Y_UNIT_TEST(SmallIntOrChar) {
+ char c = 'e';
+ i8 i = -100;
+ ui8 u = 10;
+ UNIT_ASSERT_VALUES_EQUAL(TStringBuilder() << DbgDump(c), "'e'");
+ UNIT_ASSERT_VALUES_EQUAL(TStringBuilder() << DbgDump(i), "-100");
+ UNIT_ASSERT_VALUES_EQUAL(TStringBuilder() << DbgDump(u), "10");
+ }
+}
diff --git a/library/cpp/dbg_output/ut/ya.make b/library/cpp/dbg_output/ut/ya.make
new file mode 100644
index 0000000000..201601295d
--- /dev/null
+++ b/library/cpp/dbg_output/ut/ya.make
@@ -0,0 +1,9 @@
+UNITTEST_FOR(library/cpp/dbg_output)
+
+OWNER(pg)
+
+SRCS(
+ dbg_output_ut.cpp
+)
+
+END()
diff --git a/library/cpp/dbg_output/ya.make b/library/cpp/dbg_output/ya.make
new file mode 100644
index 0000000000..7d54108f93
--- /dev/null
+++ b/library/cpp/dbg_output/ya.make
@@ -0,0 +1,15 @@
+LIBRARY()
+
+OWNER(pg)
+
+PEERDIR(
+ library/cpp/colorizer
+)
+
+SRCS(
+ dump.cpp
+ dumpers.cpp
+ engine.cpp
+)
+
+END()