27 #include <unordered_map>
34 #define ANSI_COLOR_ERROR "\x1b[31m" // Bright red - standard for errors
35 #define ANSI_COLOR_SUCCESS "\x1b[38;2;118;185;0m" // NVIDIA green (76B900 hex)
36 #define ANSI_COLOR_WARNING "\x1b[33m" // Yellow/amber - standard for warnings
37 #define ANSI_COLOR_INFO "\x1b[38;2;0;175;255m" // Cyan - better for info than blue
38 #define ANSI_COLOR_INFO2 "\x1b[38;2;147;161;161m" // Soft gray
39 #define ANSI_COLOR_FRAME "\x1b[38;2;255;165;0m" // Orange for frame
40 #define ANSI_COLOR_RESET "\x1b[0m"
59 using high_res_clock = std::chrono::high_resolution_clock;
60 using time_point = std::chrono::time_point<high_res_clock>;
63 enum class SpecialMessageType {
73 time_point start_time;
74 std::vector<std::string> info_messages;
76 std::vector<std::shared_ptr<ProcessNode>> children;
79 ProcessNode(
const std::string& n,
int frame,
bool main, ProcessNode* p)
80 : name(n), frame_num(frame), is_main_process(main), parent(p) {}
86 std::shared_ptr<ProcessNode> root_process;
87 std::stack<ProcessNode*> process_stack;
88 std::unordered_map<std::string, ProcessNode*> process_map;
91 bool needsSpacingForNextProcess;
94 bool lastMessageWasRootLevelInfo;
97 std::string getProcessKey(
const std::string& process,
int frameNum)
const {
98 return process +
"_" + std::to_string(frameNum);
102 std::string formatTime(
double ms_duration)
const {
103 std::stringstream ss;
104 ss << std::fixed << std::setprecision(2) << std::setw(7) << ms_duration <<
"ms";
109 SpecialMessageType detectSpecialMessageType(
const std::string& message)
const {
111 if (message.compare(0, 14,
"Input tensors:") == 0) {
112 return SpecialMessageType::INPUT_TENSORS;
115 if (message.compare(0, 15,
"Dynamic tensor:") == 0) {
116 return SpecialMessageType::DYNAMIC_TENSOR;
119 if (needsSpacingForNextProcess || process_stack.empty()) {
120 return SpecialMessageType::INPUT_TENSORS;
122 return SpecialMessageType::NONE;
126 std::string getIndentationForMessage(
const std::string& message,
bool isInfoLine =
true)
const {
127 SpecialMessageType messageType = detectSpecialMessageType(message);
130 if (messageType == SpecialMessageType::NONE) {
131 if (!process_stack.empty()) {
132 return getTreeIndentation(process_stack.top(), isInfoLine);
139 if (messageType == SpecialMessageType::INPUT_TENSORS) {
146 if (messageType == SpecialMessageType::DYNAMIC_TENSOR) {
149 if (!process_stack.empty()) {
150 return getTreeIndentation(process_stack.top(),
true);
161 std::string getTreeIndentation(ProcessNode* node,
bool isInfoLine =
true)
const {
162 if (!node)
return " │";
164 std::vector<ProcessNode*> path;
165 ProcessNode* current = node;
168 while (current && !current->is_main_process) {
169 path.push_back(current);
170 current = current->parent;
174 std::reverse(path.begin(), path.end());
177 std::string indent =
" │";
180 if (path.size() > 0) {
181 for (
size_t i = 0; i < path.size() - 1; i++) {
193 indent += isInfoLine ?
" └─ " :
" ├─► ";
200 std::string getCompletionIndentation(ProcessNode* node)
const {
201 if (!node)
return " │";
202 if (node->is_main_process)
return "";
204 return getTreeIndentation(node,
false);
208 std::string getSpacingIndentation(ProcessNode* node)
const {
209 if (!node)
return " │";
211 std::vector<ProcessNode*> path;
212 ProcessNode* current = node;
215 while (current && !current->is_main_process) {
216 path.push_back(current);
217 current = current->parent;
221 std::reverse(path.begin(), path.end());
224 std::string indent =
" │";
227 for (
size_t i = 0; i < path.size(); i++) {
240 LogLevel stringToLogLevel(
const std::string& level_str)
const {
241 std::string lower_str = level_str;
242 std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), ::tolower);
244 if (lower_str ==
"info" || lower_str ==
"information") {
246 }
else if (lower_str ==
"warn" || lower_str ==
"warning") {
257 root_process(nullptr),
258 needsSpacingForNextProcess(false),
259 lastMessageWasRootLevelInfo(false)
268 log_level = stringToLogLevel(level_str);
281 root_process =
nullptr;
282 process_stack = std::stack<ProcessNode*>();
285 needsSpacingForNextProcess =
false;
286 lastMessageWasRootLevelInfo =
false;
305 if (!process_stack.empty()) {
306 indent = getTreeIndentation(process_stack.top(),
true);
323 void error(
const std::string& message) {
328 if (!process_stack.empty()) {
329 indent = getTreeIndentation(process_stack.top(),
true);
346 void info(
const std::string& message) {
353 SpecialMessageType messageType = detectSpecialMessageType(message);
354 std::string indent = getIndentationForMessage(message,
true);
357 if (!process_stack.empty() && messageType == SpecialMessageType::NONE) {
358 process_stack.top()->info_messages.push_back(message);
366 if (messageType == SpecialMessageType::INPUT_TENSORS) {
367 needsSpacingForNextProcess =
false;
371 lastMessageWasRootLevelInfo = messageType == SpecialMessageType::INPUT_TENSORS;
386 std::cout << message <<
"\n";
405 bool isMainProcess =
false,
406 const std::string& additionalInfo =
"") {
413 if (frameNum != current_frame) {
414 current_frame = frameNum;
415 root_process =
nullptr;
416 process_stack = std::stack<ProcessNode*>();
418 needsSpacingForNextProcess =
false;
419 lastMessageWasRootLevelInfo =
false;
422 std::string process_key = getProcessKey(process, frameNum);
425 ProcessNode* parent =
nullptr;
426 if (!isMainProcess && !process_stack.empty()) {
427 parent = process_stack.top();
430 auto node = std::make_shared<ProcessNode>(process, frameNum, isMainProcess, parent);
431 node->start_time = high_res_clock::now();
435 parent->children.push_back(node);
436 }
else if (isMainProcess) {
442 ProcessNode* node_ptr = node.get();
443 process_map[process_key] = node_ptr;
444 process_stack.push(node_ptr);
450 std::cout <<
"\n" << process <<
" Frame-" << frameNum
451 <<
" │ ⚡ Starting" <<
"\n"
454 std::cout <<
"\n" << process
455 <<
" │ ⚡ Starting" <<
"\n"
459 needsSpacingForNextProcess =
false;
460 lastMessageWasRootLevelInfo =
false;
463 if (needsSpacingForNextProcess && parent) {
464 std::string spacing = getSpacingIndentation(parent);
465 std::cout << spacing <<
" │\n";
470 if (!isMainProcess && parent && process_stack.size() > 1) {
471 std::string spacing = getSpacingIndentation(parent);
472 std::cout << spacing <<
" │\n";
477 if (lastMessageWasRootLevelInfo && process_stack.size() == 1) {
478 std::cout <<
" │ │\n";
482 std::string indentation = getTreeIndentation(node_ptr,
false);
483 std::cout << indentation << process <<
" │ ⚡ Starting" <<
"\n";
486 needsSpacingForNextProcess =
false;
487 lastMessageWasRootLevelInfo =
false;
491 if (!additionalInfo.empty()) {
492 node->info_messages.push_back(additionalInfo);
493 std::string info_indent = getTreeIndentation(node_ptr,
true);
494 std::cout << info_indent
512 bool isMainProcess =
false,
513 const std::string& additionalInfo =
"") {
519 std::string process_key = getProcessKey(process, frameNum);
520 auto it = process_map.find(process_key);
522 if (it == process_map.end()) {
527 ProcessNode* node = it->second;
530 auto end_time = high_res_clock::now();
531 auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
532 end_time - node->start_time);
533 double ms_duration = duration.count() / 1000.0;
536 std::string timing_info = formatTime(ms_duration);
547 std::cout << process <<
" Frame-" << frameNum
548 <<
" │ ⏱ " << std::right << timing_info
549 <<
" │ " << (success ? successSymbol : failureSymbol) <<
" "
550 << (success ?
"Completed" :
"Failed") <<
"\n";
553 <<
" │ ⏱ " << std::right << timing_info
554 <<
" │ " << (success ? successSymbol : failureSymbol) <<
" "
555 << (success ?
"Completed" :
"Failed") <<
"\n";
559 std::string indentation = getCompletionIndentation(node);
562 std::cout << indentation << process
563 <<
" │ ⏱ " << std::right << timing_info
564 <<
" │ " << (success ? successSymbol : failureSymbol) <<
" "
565 << (success ?
"Completed" :
"Failed") <<
"\n";
568 needsSpacingForNextProcess =
true;
573 std::string spacing = getSpacingIndentation(node->parent);
574 std::cout << spacing <<
" │\n";
579 if (!additionalInfo.empty()) {
580 std::string info_indent = getTreeIndentation(node,
true);
581 std::cout << info_indent
586 if (!process_stack.empty() && process_stack.top() == node) {
591 process_map.erase(process_key);