Project
Loading...
Searching...
No Matches
FrameworkGUIDebugger.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
14#include "DebugGUI/imgui.h"
15#include "DebugGUI/implot.h"
16#include "DebugGUI/imgui_extras.h"
24#include "PaletteHelpers.h"
25#include "FrameworkGUIState.h"
26#include "Framework/Signpost.h"
27#include <DebugGUI/icons_font_awesome.h>
28
29#include <fmt/format.h>
30
31#include <algorithm>
32#include <iostream>
33#include <string>
34#include <cinttypes>
35#include <numeric>
36
38// Make sure we can use aggregated initialisers.
39#pragma GCC diagnostic push
40#pragma GCC diagnostic ignored "-Wpedantic"
41
42// Simplify debugging
43template class std::vector<o2::framework::DeviceMetricsInfo>;
44
45namespace o2::framework::gui
46{
47// Type erased information for the plotting
49 int mod;
50 size_t first;
51 size_t size;
52 const void* Y = nullptr;
53 const void* X = nullptr;
55 const char* legend = nullptr;
56 int axis = 0;
57};
58
59} // namespace o2::framework::gui
60
61template class std::vector<o2::framework::gui::MultiplotData>;
62
63namespace o2::framework::gui
64{
65
67{
68 switch (logLevel) {
69 case LogParsingHelpers::LogLevel::Info:
70 return PaletteHelpers::GREEN;
71 case LogParsingHelpers::LogLevel::Important:
72 return PaletteHelpers::GREEN;
73 case LogParsingHelpers::LogLevel::Debug:
74 return PaletteHelpers::WHITE;
75 case LogParsingHelpers::LogLevel::Alarm:
76 return PaletteHelpers::YELLOW;
77 case LogParsingHelpers::LogLevel::Warning:
78 return PaletteHelpers::DARK_YELLOW;
79 case LogParsingHelpers::LogLevel::Error:
80 return PaletteHelpers::RED;
81 case LogParsingHelpers::LogLevel::Critical:
82 return PaletteHelpers::RED;
83 case LogParsingHelpers::LogLevel::Fatal:
84 return PaletteHelpers::RED;
85 case LogParsingHelpers::LogLevel::Unknown:
86 return PaletteHelpers::DARK_RED;
87 default:
88 return PaletteHelpers::DARK_RED;
89 };
90}
91
92void displayHistory(const DeviceInfo& info, DeviceControl& control)
93{
94 if (info.history.empty()) {
95 return;
96 }
97 int startPos = info.historyPos;
98 const int historySize = info.history.size();
99
100 int triggerStartPos = startPos + 1 % historySize;
101 int triggerStopPos = startPos % historySize;
102
103 int j = startPos;
104 // We look for a stop trigger, so that we know where to stop the search for
105 // out start search. If no stop trigger is found, we search until the end
106 if (control.logStopTrigger[0]) {
107 while ((j % historySize) != ((startPos + 1) % historySize)) {
108 assert(j >= 0);
109 assert(j < historySize);
110 auto& line = info.history[j];
111 if (strstr(line.c_str(), control.logStopTrigger)) {
112 triggerStopPos = (j + 1) % historySize;
113 break;
114 }
115 // Wrap in case we end up below 0
116 j = (j == 0) ? historySize - 1 : j - 1;
117 }
118 }
119
120 // Look for the last instance of the start trigger before the
121 // last stop trigger.
122 j = startPos + 1;
123 if (control.logStartTrigger[0]) {
124 while ((j % historySize) != triggerStopPos) {
125 assert(historySize > j);
126 assert(historySize == 1000);
127 auto& line = info.history[j];
128 if (strstr(line.c_str(), control.logStartTrigger)) {
129 triggerStartPos = j;
130 }
131 j = (j + 1) % historySize;
132 }
133 }
134
135 // We start from the last trigger found. Eventually this is the first
136 // line in the ring buffer, if no trigger is specified.
137 size_t ji = triggerStartPos % historySize;
138 size_t je = triggerStopPos % historySize;
139 size_t iterations = 0;
140 while (historySize && ((ji % historySize) != je)) {
141 assert(iterations < historySize);
142 iterations++;
143 assert(historySize == 1000);
144 assert(ji < historySize);
145 auto& line = info.history[ji];
146 auto logLevel = info.historyLevel[ji];
147
148 // Skip empty lines
149 if (line.empty()) {
150 ji = (ji + 1) % historySize;
151 continue;
152 }
153 // Print matching lines. Notice we filter twice, once on input, to reduce the
154 // stream, a second time at display time, to avoid showing unrelevant
155 // messages from past.
156 if (strstr(line.c_str(), control.logFilter) != nullptr) {
157 if (logLevel >= control.logLevel) {
158 ImGui::TextColored(colorForLogLevel(logLevel), "%s", line.c_str());
159 }
160 }
161 ji = (ji + 1) % historySize;
162 }
163}
164
165struct HistoData {
166 int mod;
167 size_t first;
168 size_t size;
169 void* points = nullptr;
170 const size_t* time = nullptr;
171 char const* legend = nullptr;
172};
173
174enum struct MetricsDisplayStyle : int {
175 Lines = 0,
176 Histos = 1,
177 Sparks = 2,
178 Table = 3,
179 Stems = 4
180};
181
184 std::string label;
185};
186
188 int axis = 0; // The Axis to use for Y
189 bool visible = false;
190 bool selected = false;
191 std::string legend;
192 size_t legendHash = -1;
193};
194
200};
201
207
208// We use this to keep together all the kind of metrics
209// so that we can display driver and device metrics in the same plot
210// without an if.
212 std::span<DeviceMetricsInfo const> metrics[TOTAL_TYPES_OF_METRICS];
213 std::span<TopologyNodeInfo const> specs[TOTAL_TYPES_OF_METRICS];
214};
215
217 double startTime,
218 std::vector<MetricIndex>& visibleMetricsIndex,
219 std::vector<MetricDisplayState>& metricDisplayStates,
220 AllMetricsStore const& metricStore)
221{
222 static bool locked = false;
223 ImGui::Checkbox("Lock scrolling", &locked);
224 ImGui::BeginTable("##sparks table", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, ImVec2{-1, -1});
225 ImGui::TableSetupColumn("##close button", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 25);
226 ImGui::TableSetupColumn("##plot name", ImGuiTableColumnFlags_WidthFixed, 200);
227 ImGui::TableSetupColumn("##plot", ImGuiTableColumnFlags_WidthFixed, 0);
228 ImGui::TableSetupScrollFreeze(2, 0);
229 for (size_t i = 0; i < visibleMetricsIndex.size(); ++i) {
230 auto& index = visibleMetricsIndex[i];
231 auto& metricsInfos = metricStore.metrics[index.storeIndex];
232 auto& metricsInfo = metricsInfos[index.deviceIndex];
233 auto& metric = metricsInfo.metrics[index.metricIndex];
234 auto& state = metricDisplayStates[index.stateIndex];
235
236 ImGui::TableNextColumn();
237 ImGui::PushID(index.stateIndex);
238 state.visible = !ImGui::Button("-");
239 ImGui::PopID();
240 ImGui::TableNextColumn();
241 ImGui::TextUnformatted(state.legend.c_str());
242 ImGui::TableNextColumn();
243 static ImPlotAxisFlags rtx_axis = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickMarks;
244 static ImPlotAxisFlags rty_axis = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoTickMarks;
245 ImGui::PushID(index.stateIndex);
247 data.mod = std::min(metric.filledMetrics, metricStorageSize(metric.type));
248 data.first = metric.pos - data.mod;
249 data.size = metric.filledMetrics;
250 data.legend = state.legend.c_str();
251
252 if (!locked) {
253 ImPlot::SetNextAxisLimits(ImAxis_X1, (startTime + ImGui::GetTime() - 100) * 1000, (startTime + ImGui::GetTime()) * 1000, ImGuiCond_Always);
254 ImPlot::SetNextAxisLimits(ImAxis_Y1, metricsInfo.min[index.metricIndex], metricsInfo.max[index.metricIndex] * 1.1, ImGuiCond_Always);
255 rty_axis |= ImPlotAxisFlags_LockMin;
256 }
257 if (ImPlot::BeginPlot("##sparks", ImVec2(700, 100), 0)) {
258 ImPlot::SetupAxes("time", "value", rtx_axis, rty_axis);
259 ImPlot::SetAxis(state.axis);
260 switch (metric.type) {
261 case MetricType::Enum: {
262 data.points = (void*)metricsInfo.enumMetrics[metric.storeIdx].data();
263 data.time = metricsInfo.enumTimestamps[metric.storeIdx].data();
264
265 auto getter = [](int idx, void* hData) -> ImPlotPoint {
266 auto histoData = reinterpret_cast<HistoData*>(hData);
267 size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod;
268 assert(pos >= 0 && pos < metricStorageSize(MetricType::Enum));
269 return ImPlotPoint(histoData->time[pos], ((int8_t*)(histoData->points))[pos]);
270 };
271 ImPlot::PlotLineG("##plot", getter, &data, data.mod);
272 } break;
273 case MetricType::Int: {
274 data.points = (void*)metricsInfo.intMetrics[metric.storeIdx].data();
275 data.time = metricsInfo.intTimestamps[metric.storeIdx].data();
276
277 auto getter = [](int idx, void* hData) -> ImPlotPoint {
278 auto histoData = reinterpret_cast<HistoData*>(hData);
279 size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod;
280 assert(pos >= 0 && pos < metricStorageSize(MetricType::Int));
281 return ImPlotPoint(histoData->time[pos], ((int*)(histoData->points))[pos]);
282 };
283 ImPlot::PlotLineG("##plot", getter, &data, data.mod);
284 } break;
285 case MetricType::Uint64: {
286 data.points = (void*)metricsInfo.uint64Metrics[metric.storeIdx].data();
287 data.time = metricsInfo.uint64Timestamps[metric.storeIdx].data();
288
289 auto getter = [](int idx, void* hData) -> ImPlotPoint {
290 auto histoData = reinterpret_cast<HistoData*>(hData);
291 size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod;
292 assert(pos >= 0 && pos < metricStorageSize(MetricType::Uint64));
293 return ImPlotPoint(histoData->time[pos], ((uint64_t*)histoData->points)[pos]);
294 };
295 ImPlot::PlotLineG("##plot", getter, &data, data.mod, 0);
296 } break;
297 case MetricType::Float: {
298 data.points = (void*)metricsInfo.floatMetrics[metric.storeIdx].data();
299 data.time = metricsInfo.floatTimestamps[metric.storeIdx].data();
300
301 auto getter = [](int idx, void* hData) -> ImPlotPoint {
302 auto histoData = reinterpret_cast<HistoData*>(hData);
303 size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod;
304 assert(pos >= 0 && pos < metricStorageSize(MetricType::Float));
305 return ImPlotPoint(histoData->time[pos], ((float*)histoData->points)[pos]);
306 };
307 ImPlot::PlotLineG("##plot", getter, &data, data.mod, 0);
308 } break;
309 default:
310 return;
311 break;
312 }
313 ImPlot::EndPlot();
314 }
315 ImGui::PopID();
316 }
317 ImGui::EndTable();
318}
319
320int formatSI(double value, char* buff, int size, void* user_data)
321{
322 if (value == 0.0) {
323 return snprintf(buff, size, "%.0f", value);
324 }
325 if (value < 10.0) {
326 return snprintf(buff, size, "%.2f", value);
327 }
328 if (value < 1000.0) {
329 return snprintf(buff, size, "%.0f", value);
330 }
331 if (value < 1000000.0) {
332 return snprintf(buff, size, "%.0f k", value / 1000.0);
333 }
334 if (value < 1000000000.0) {
335 return snprintf(buff, size, "%.0f M", value / 1000000.0);
336 }
337 if (value < 1000000000000.0) {
338 return snprintf(buff, size, "%.0f G", value / 1000000000.0);
339 }
340 return snprintf(buff, size, "%.0f T", value / 1000000000000.0);
341}
342
343int formatTimeSinceStart(double value, char* buff, int size, void* user_data)
344{
345 auto* startTime = (int64_t*)user_data;
346 if (value - *startTime < 0) {
347 buff[0] = '\0';
348 return 0;
349 }
350 int64_t seconds = (value - *startTime) / 1000;
351 int64_t minutes = seconds / 60;
352 return snprintf(buff, size, "%02" PRIi64 ":%02" PRIi64, minutes, seconds % 60);
353}
354
356 size_t rangeBegin, size_t rangeEnd, size_t bins, MetricsDisplayStyle displayType,
357 std::vector<MetricDisplayState>& state,
358 AllMetricsStore const& metricStore,
359 DriverInfo const& driverInfo)
360{
361 std::vector<void*> metricsToDisplay;
362 std::vector<const char*> deviceNames;
363 std::vector<MultiplotData> userData;
364#ifdef NDEBUG
365 for (size_t si = 0; si < TOTAL_TYPES_OF_METRICS; ++si) {
366 assert(metricsStore.metrics[si].size() == metricStore.specs[si].size());
367 }
368#endif
369 float maxValue[3] = {std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest()};
370 float minValue[3] = {0, 0, 0};
371 size_t maxDomain = std::numeric_limits<size_t>::lowest();
372 size_t minDomain = std::numeric_limits<size_t>::max();
373 size_t gmi = 0;
374
375 ImPlotAxisFlags axisFlags = 0;
376
377 for (size_t si = 0; si < TOTAL_TYPES_OF_METRICS; ++si) {
378 std::span<DeviceMetricsInfo const> metricsInfos = metricStore.metrics[si];
379 std::span<TopologyNodeInfo const> specs = metricStore.specs[si];
380 for (int di = 0; di < metricsInfos.size(); ++di) {
381 for (size_t mi = 0; mi < metricsInfos[di].metrics.size(); ++mi) {
382 if (state[gmi].visible == false) {
383 gmi++;
384 continue;
385 }
386 auto& metric = metricsInfos[di].metrics[mi];
387 deviceNames.push_back(specs[di].label.c_str());
389 data.size = metric.filledMetrics;
390 data.legend = state[gmi].legend.c_str();
391 data.type = metric.type;
392 data.axis = state[gmi].axis;
393 minValue[data.axis] = std::min(minValue[data.axis], metricsInfos[di].min[mi]);
394 maxValue[data.axis] = std::max(maxValue[data.axis], metricsInfos[di].max[mi]);
395 minDomain = std::min(minDomain, metricsInfos[di].minDomain[mi]);
396 maxDomain = std::max(maxDomain, metricsInfos[di].maxDomain[mi]);
397 axisFlags |= data.axis == 1 ? (ImPlotFlags_)ImPlotFlags_YAxis2 : ImPlotFlags_None;
398 axisFlags |= data.axis == 2 ? (ImPlotFlags_)ImPlotFlags_YAxis3 : ImPlotFlags_None;
399 switch (metric.type) {
400 case MetricType::Int: {
401 data.Y = metricsInfos[di].intMetrics[metric.storeIdx].data();
402 auto& timestamps = metricsInfos[di].intTimestamps[metric.storeIdx];
403 data.mod = std::min(metric.filledMetrics, timestamps.size());
404 data.first = metric.pos - data.mod;
405 data.X = timestamps.data();
406 } break;
407 case MetricType::Enum: {
408 data.Y = metricsInfos[di].enumMetrics[metric.storeIdx].data();
409 auto& timestamps = metricsInfos[di].enumTimestamps[metric.storeIdx];
410 data.mod = std::min(metric.filledMetrics, timestamps.size());
411 data.first = metric.pos - data.mod;
412 data.X = timestamps.data();
413 } break;
414 case MetricType::Uint64: {
415 data.Y = metricsInfos[di].uint64Metrics[metric.storeIdx].data();
416 auto& timestamps = metricsInfos[di].uint64Timestamps[metric.storeIdx];
417 data.mod = std::min(metric.filledMetrics, timestamps.size());
418 data.first = metric.pos - data.mod;
419 data.X = timestamps.data();
420 } break;
421 case MetricType::Float: {
422 data.Y = metricsInfos[di].floatMetrics[metric.storeIdx].data();
423 auto& timestamps = metricsInfos[di].floatTimestamps[metric.storeIdx];
424 data.mod = std::min(metric.filledMetrics, timestamps.size());
425 data.first = metric.pos - data.mod;
426 data.X = timestamps.data();
427 } break;
428 case MetricType::Unknown:
429 case MetricType::String: {
430 data.Y = nullptr;
431 data.type = MetricType::String;
432 } break;
433 }
434
435 userData.emplace_back(data);
436 gmi++;
437 }
438 }
439 }
440
441 maxDomain = std::max(minDomain + 1024, maxDomain);
442 for (size_t ai = 0; ai < 3; ++ai) {
443 maxValue[ai] = std::max(minValue[ai] + 1.f, maxValue[ai]);
444 }
445
446 // Nothing to show.
447 if (userData.empty()) {
448 return;
449 }
450 for (size_t ui = 0; ui < userData.size(); ++ui) {
451 metricsToDisplay.push_back(&(userData[ui]));
452 }
453
454 auto getterXY = [](int idx, void* hData) -> ImPlotPoint {
455 auto histoData = reinterpret_cast<const MultiplotData*>(hData);
456 size_t pos = (histoData->first + static_cast<size_t>(idx)) % histoData->mod;
457 double x = static_cast<const size_t*>(histoData->X)[pos];
458 double y = 0.;
459 if (histoData->type == MetricType::Int) {
460 y = static_cast<const int*>(histoData->Y)[pos];
461 } else if (histoData->type == MetricType::Uint64) {
462 y = static_cast<const uint64_t*>(histoData->Y)[pos];
463 } else if (histoData->type == MetricType::Float) {
464 y = static_cast<const float*>(histoData->Y)[pos];
465 } else if (histoData->type == MetricType::Enum) {
466 y = static_cast<const int8_t*>(histoData->Y)[pos];
467 }
468 auto point = ImPlotPoint{x, y};
469 return point;
470 };
471 static bool logScale = false;
472 ImGui::Checkbox("Log scale", &logScale);
473
474 ImPlot::SetNextAxisLimits(ImAxis_X1, minDomain, maxDomain, ImGuiCond_Once);
475
476 auto axisPadding = 0.;
477 if (displayType == MetricsDisplayStyle::Lines) {
478 axisPadding = 0.2;
479 }
480
481 ImPlot::SetNextAxisLimits(ImAxis_Y1, minValue[0] - (maxValue[0] - minValue[0]) * axisPadding,
482 maxValue[0] * (1. + axisPadding), ImGuiCond_Always);
483 ImPlot::SetNextAxisLimits(ImAxis_Y2, minValue[1] - (maxValue[1] - minValue[1]) * axisPadding,
484 maxValue[1] * (1. + axisPadding), ImGuiCond_Always);
485 ImPlot::SetNextAxisLimits(ImAxis_Y3, minValue[2] - (maxValue[2] - minValue[2]) * axisPadding,
486 maxValue[2] * (1. + axisPadding), ImGuiCond_Always);
487
488 switch (displayType) {
489 case MetricsDisplayStyle::Histos:
490 if (ImPlot::BeginPlot("##Some plot")) {
491 ImPlot::SetupAxes("time", "value");
492 ImPlot::SetupAxisFormat(ImAxis_Y1, formatSI, nullptr);
493 ImPlot::SetupAxisFormat(ImAxis_Y2, formatSI, nullptr);
494 ImPlot::SetupAxisFormat(ImAxis_Y3, formatSI, nullptr);
495 ImPlot::SetupAxisFormat(ImAxis_X1, formatTimeSinceStart, (void*)&driverInfo.startTimeMsFromEpoch);
496 for (size_t pi = 0; pi < metricsToDisplay.size(); ++pi) {
497 ImGui::PushID(pi);
498 auto data = (const MultiplotData*)metricsToDisplay[pi];
499 const char* label = ((MultiplotData*)metricsToDisplay[pi])->legend;
500 ImPlot::PlotBarsG(label, getterXY, metricsToDisplay[pi], data->mod, 1, 0);
501 ImGui::PopID();
502 }
503 ImPlot::EndPlot();
504 }
505
506 break;
507 case MetricsDisplayStyle::Lines: {
508 auto xAxisFlags = ImPlotAxisFlags_None;
509 auto yAxisFlags = ImPlotAxisFlags_LockMin;
510 // ImPlot::FitNextPlotAxes(true, true, true, true);
511 if (ImPlot::BeginPlot("##Some plot", {-1, -1}, axisFlags)) {
512 ImPlot::SetupAxes("time", "value", xAxisFlags, yAxisFlags);
513 ImPlot::SetupAxisFormat(ImAxis_Y1, formatSI, nullptr);
514 ImPlot::SetupAxisFormat(ImAxis_Y2, formatSI, nullptr);
515 ImPlot::SetupAxisFormat(ImAxis_Y3, formatSI, nullptr);
516 ImPlot::SetupAxisFormat(ImAxis_X1, formatTimeSinceStart, (void*)&driverInfo.startTimeMsFromEpoch);
517 for (size_t pi = 0; pi < metricsToDisplay.size(); ++pi) {
518 ImGui::PushID(pi);
519 auto data = (const MultiplotData*)metricsToDisplay[pi];
520 ImPlot::SetAxis(data->axis);
521 ImPlot::PlotLineG(data->legend, getterXY, metricsToDisplay[pi], data->mod, 0);
522 ImGui::PopID();
523 }
524 ImPlot::EndPlot();
525 }
526 } break;
527 case MetricsDisplayStyle::Stems:
528 if (ImPlot::BeginPlot("##Some plot")) {
529 ImPlot::SetupAxes("time", "value");
530 for (size_t pi = 0; pi < userData.size(); ++pi) {
531 auto data = reinterpret_cast<const MultiplotData*>(metricsToDisplay[pi]);
532 // FIXME: display a message for other metrics
533 if (data->type == MetricType::Uint64) {
534 ImGui::PushID(pi);
535 ImPlot::PlotScatterG(((MultiplotData*)metricsToDisplay[pi])->legend, getterXY, metricsToDisplay[pi], data->mod, 0);
536 ImGui::PopID();
537 }
538 }
539 ImPlot::EndPlot();
540 }
541 break;
542 default:
543 break;
544 }
545}
546
551
552void metricsTableRow(std::vector<MetricIndex> metricIndex,
553 AllMetricsStore const& metricsStore,
554 int row)
555{
556 ImGui::TableNextColumn();
557 ImGui::Text("%d", row);
558
559 for (auto index : metricIndex) {
560 auto& metricsInfos = metricsStore.metrics[index.storeIndex];
561 auto& metricsInfo = metricsInfos[index.deviceIndex];
562 auto& info = metricsInfos[index.deviceIndex].metrics[index.metricIndex];
563
564 ImGui::TableNextColumn();
565 if (info.filledMetrics <= row) {
566 ImGui::Text(" - ");
567 continue;
568 }
569 switch (info.type) {
570 case MetricType::Int: {
571 auto time = metricsInfo.intTimestamps[info.storeIdx][row];
572 ImGui::Text("%i, %" PRIu64, metricsInfo.intMetrics[info.storeIdx][row], (uint64_t)time);
573 } break;
574 case MetricType::Uint64: {
575 auto time = metricsInfo.uint64Timestamps[info.storeIdx][row];
576 ImGui::Text("%" PRIu64 ", %" PRIu64, metricsInfo.uint64Metrics[info.storeIdx][row], (uint64_t)time);
577 } break;
578 case MetricType::Float: {
579 auto time = metricsInfo.floatTimestamps[info.storeIdx][row];
580 ImGui::Text("%f, %" PRIu64, metricsInfo.floatMetrics[info.storeIdx][row], (uint64_t)time);
581 } break;
582 case MetricType::Enum: {
583 auto time = metricsInfo.enumTimestamps[info.storeIdx][row];
584 ImGui::Text("%i, %" PRIu64, metricsInfo.enumMetrics[info.storeIdx][row], (uint64_t)time);
585 } break;
586 case MetricType::String: {
587 auto time = metricsInfo.stringTimestamps[info.storeIdx][row];
588 ImGui::Text("%s, %" PRIu64, metricsInfo.stringMetrics[info.storeIdx][row].data, (uint64_t)time);
589 } break;
590 default:
591 break;
592 }
593 }
594}
595
596bool hasAll(const char* s, const char* q)
597{
598 /* base case: empty query */
599 if (*q == 0) {
600 return true;
601 }
602 do {
603 s = strchr(s, (int)*q);
604 if (s == nullptr) {
605 return false;
606 }
607 s++;
608 q++;
609 } while ((int)*q != 0);
610 return true;
611}
612
613void TextCenter(char const* text)
614{
615 float font_size = ImGui::GetFontSize() * strlen(text) / 2;
616 ImGui::Dummy(ImVec2(
617 ImGui::GetWindowSize().x / 2 -
618 font_size + (font_size / 2),
619 120));
620
621 ImGui::TextUnformatted(text);
622}
623
625 DriverInfo const& driverInfo,
626 std::vector<DeviceInfo> const& infos,
627 std::vector<DataProcessorInfo> const& metadata,
628 std::vector<DeviceControl>& controls,
629 AllMetricsStore const& metricsStore)
630{
631 if (state.bottomPaneVisible == false) {
632 return;
633 }
634 auto metricDisplayPos = 0;
635 static bool metricSelectorVisible = true;
636 static std::vector<MetricDisplayState> metricDisplayState;
637
638 // Calculate the full timestamp range for the selected metric
639 size_t minTime = -1;
640 size_t maxTime = 0;
641 constexpr size_t MAX_QUERY_SIZE = 256;
642 static char query[MAX_QUERY_SIZE];
643 static char lastSelectedQuery[MAX_QUERY_SIZE];
644
645 size_t totalMetrics = 0;
646 for (auto& metricsInfos : metricsStore.metrics) {
647 for (auto& metricInfo : metricsInfos) {
648 totalMetrics += metricInfo.metrics.size();
649 }
650 }
651
652 if (totalMetrics != metricDisplayState.size() || strcmp(lastSelectedQuery, query)) {
653 size_t gmi = 0;
654 std::vector<MetricDisplayState> newMetricDisplayStates;
655 newMetricDisplayStates.resize(totalMetrics);
656 static std::vector<int> order;
657 order.resize(metricDisplayState.size());
658 std::iota(order.begin(), order.end(), 0);
659 std::sort(order.begin(), order.end(), [](int a, int b) {
660 return metricDisplayState[a].legend < metricDisplayState[b].legend;
661 });
662
663 for (size_t si = 0; si < TOTAL_TYPES_OF_METRICS; ++si) {
664 auto& metricsInfos = metricsStore.metrics[si];
665 auto& specs = metricsStore.specs[si];
666 for (size_t di = 0; di < metricsInfos.size(); ++di) {
667 auto& metricInfo = metricsInfos[di];
668 auto& spec = specs[di];
669 for (size_t li = 0; li != metricInfo.metricLabels.size(); ++li) {
670 char const* metricLabel = metricInfo.metricLabels[li].label;
671 // find the equal range for the label
672 auto low = std::lower_bound(order.begin(), order.end(), spec.label, [](int a, std::string const& b) {
673 return metricDisplayState[a].legend < b;
674 });
675 auto up = std::upper_bound(low, order.end(), spec.label, [](std::string const& a, int b) {
676 return a < metricDisplayState[b].legend;
677 });
678 std::string legend = fmt::format("{}/{}", spec.label, metricLabel);
679 auto old = std::lower_bound(low, up, legend, [](int a, std::string const& b) {
680 return metricDisplayState[a].legend < b;
681 });
682 if (old != order.end() && metricDisplayState[*old].legend == legend) {
683 newMetricDisplayStates[gmi].visible = metricDisplayState[*old].visible;
684 newMetricDisplayStates[gmi].axis = metricDisplayState[*old].axis;
685 } else {
686 newMetricDisplayStates[gmi].visible = false;
687 }
688
689 newMetricDisplayStates[gmi].selected = hasAll(metricLabel, query);
690 newMetricDisplayStates[gmi].legend = legend;
691 gmi++;
692 }
693 }
694 }
695 metricDisplayState.swap(newMetricDisplayStates);
696 strcpy(lastSelectedQuery, query);
697 }
698
699 static std::vector<MetricIndex> selectedMetricIndex;
700
701 if (metricSelectorVisible) {
702 selectedMetricIndex.clear();
703 size_t gmi = 0;
704 for (size_t si = 0; si < TOTAL_TYPES_OF_METRICS; ++si) {
705 auto& metricsInfos = metricsStore.metrics[si];
706
707 for (size_t di = 0; di < metricsInfos.size(); ++di) {
708 auto& metricInfo = metricsInfos[di];
709 for (size_t li = 0; li != metricInfo.metricLabels.size(); ++li) {
710 auto& state = metricDisplayState[gmi];
711 if (state.selected) {
712 selectedMetricIndex.emplace_back(MetricIndex{si, di, li, gmi});
713 }
714 gmi++;
715 }
716 }
717 }
718
719 metricDisplayPos = ImGui::GetIO().DisplaySize.x / 4;
720 ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetIO().DisplaySize.y - state.bottomPaneSize), 0);
721 ImGui::SetNextWindowSize(ImVec2(metricDisplayPos, state.bottomPaneSize), 0);
722 ImGui::Begin("Available metrics", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
723
724 ImGui::Text(ICON_FA_FILTER);
725 ImGui::SameLine();
726 ImGui::InputText("##query-metrics", query, MAX_QUERY_SIZE);
727 size_t metricSize = 0;
728 for (auto deviceMetrics : metricsStore.metrics) {
729 metricSize += DeviceMetricsInfoHelpers::metricsStorageSize(deviceMetrics);
730 }
731 // ImGui::Text("Total memory used %zu MB", metricSize / 1000000);
732 ImGui::Text("%zu/%zu matching", selectedMetricIndex.size(), totalMetrics);
733
734 static const char* possibleAxis[] = {
735 "Y",
736 "Y1",
737 "Y2",
738 };
739 if (ImGui::BeginTable("##metrics-table", 3, ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2{-1, -1})) {
740 ImGui::TableSetupColumn("##close button", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 20);
741 ImGui::TableSetupColumn("##axis kind", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 30);
742 ImGui::TableSetupScrollFreeze(1, 0);
743 ImGuiListClipper clipper;
744 clipper.Begin(selectedMetricIndex.size());
745 while (clipper.Step()) {
746 for (size_t i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
747 auto& index = selectedMetricIndex[i];
748 auto& metricsInfos = metricsStore.metrics[index.storeIndex];
749 auto& nodes = metricsStore.specs[index.storeIndex];
750 auto& metricInfo = metricsInfos[index.deviceIndex];
751 auto& node = nodes[index.deviceIndex];
752 auto& label = metricInfo.metricLabels[index.metricIndex];
753 ImGui::PushID(index.stateIndex);
754 ImGui::TableNextRow();
755 ImGui::TableNextColumn();
756 ImGui::Checkbox("##checkbox", &metricDisplayState[index.stateIndex].visible);
757 ImGui::TableNextColumn();
758 if (metricDisplayState[index.stateIndex].visible) {
759 if (ImGui::BeginCombo("##Select style", possibleAxis[metricDisplayState[index.stateIndex].axis], ImGuiComboFlags_NoArrowButton)) {
760 for (int n = 0; n < IM_ARRAYSIZE(possibleAxis); n++) {
761 bool is_selected = (metricDisplayState[index.stateIndex].axis == n);
762 if (ImGui::Selectable(possibleAxis[n], is_selected)) {
763 metricDisplayState[index.stateIndex].axis = n;
764 }
765 if (is_selected) {
766 ImGui::SetItemDefaultFocus(); // You may set the initial focus when opening the combo (scrolling + for keyboard navigation support)
767 }
768 }
769 ImGui::EndCombo();
770 }
771 }
772 ImGui::TableNextColumn();
773 ImGui::Text("%s/%s", node.label.c_str(), label.label);
774 ImGui::PopID();
775 }
776 }
777 ImGui::EndTable();
778 }
779 ImGui::End();
780 }
781 ImGui::SetNextWindowPos(ImVec2(metricDisplayPos, ImGui::GetIO().DisplaySize.y - state.bottomPaneSize), 0);
782 ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x - metricDisplayPos, state.bottomPaneSize), 0);
783
784 ImGui::Begin("Devices", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
785
786 if (!metricSelectorVisible) {
787 metricSelectorVisible = ImGui::Button(ICON_FA_CHEVRON_RIGHT "Show metric selector");
788 } else {
789 metricSelectorVisible = !ImGui::Button(ICON_FA_CHEVRON_LEFT "Hide metric selector");
790 }
791 static char const* plotStyles[] = {
792 "lines",
793 "histograms",
794 "sparks",
795 "table",
796 "stems"};
797 ImGui::SameLine();
798 static enum MetricsDisplayStyle currentStyle = MetricsDisplayStyle::Lines;
799 static char const* currentStyleStr = plotStyles[0];
800 ImGui::TextUnformatted("Metric display style:");
801 ImGui::SameLine();
802 ImGui::PushItemWidth(100);
803 if (ImGui::BeginCombo("##Select style", currentStyleStr)) {
804 for (int n = 0; n < IM_ARRAYSIZE(plotStyles); n++) {
805 bool is_selected = (currentStyleStr == plotStyles[n]); // You can store your selection however you want, outside or inside your objects
806 if (ImGui::Selectable(plotStyles[n], is_selected)) {
807 currentStyleStr = plotStyles[n];
808 currentStyle = (MetricsDisplayStyle)n;
809 }
810 if (is_selected) {
811 ImGui::SetItemDefaultFocus(); // You may set the initial focus when opening the combo (scrolling + for keyboard navigation support)
812 }
813 }
814 ImGui::EndCombo();
815 }
816 ImGui::PopItemWidth();
817
818 size_t gmi = 0;
819 int visibleMetrics = 0;
820 static std::vector<int> visibleDevicesIndex;
821 static std::vector<MetricIndex> visibleMetricsIndex;
822
823 visibleDevicesIndex.reserve(totalMetrics);
824 visibleDevicesIndex.clear();
825 visibleMetricsIndex.clear();
826
827 for (size_t si = 0; si < TOTAL_TYPES_OF_METRICS; ++si) {
828 auto& metricsInfos = metricsStore.metrics[si];
829 for (size_t di = 0; di < metricsInfos.size(); ++di) {
830 auto& metricInfo = metricsInfos[di];
831 bool deviceVisible = false;
832 for (size_t mi = 0; mi < metricInfo.metrics.size(); ++mi) {
833 auto& state = metricDisplayState[gmi];
834 if (state.visible) {
835 deviceVisible = true;
836 visibleMetrics++;
837 auto& metric = metricInfo.metrics[mi];
838 size_t const* timestamps = nullptr;
839 switch (metric.type) {
840 case MetricType::Int:
841 timestamps = metricInfo.intTimestamps[metric.storeIdx].data();
842 break;
843 case MetricType::Float:
844 timestamps = metricInfo.floatTimestamps[metric.storeIdx].data();
845 break;
846 case MetricType::String:
847 timestamps = metricInfo.stringTimestamps[metric.storeIdx].data();
848 break;
849 case MetricType::Uint64:
850 timestamps = metricInfo.uint64Timestamps[metric.storeIdx].data();
851 break;
852 case MetricType::Enum:
853 timestamps = metricInfo.enumTimestamps[metric.storeIdx].data();
854 break;
855 default:
856 throw std::runtime_error("Unknown metric type");
857 }
858
859 for (size_t ti = 0; ti != metricStorageSize(metric.type); ++ti) {
860 size_t minRangePos = (metric.pos + ti) % metricStorageSize(metric.type);
861 size_t curMinTime = timestamps[minRangePos];
862 if (curMinTime == 0) {
863 continue;
864 }
865 minTime = minTime < curMinTime ? minTime : curMinTime;
866 if (minTime != 0 && minTime != -1) {
867 break;
868 }
869 }
870 size_t maxRangePos = (size_t)(metric.pos) - 1 % metricStorageSize(metric.type);
871 size_t curMaxTime = timestamps[maxRangePos];
872 maxTime = std::max(maxTime, curMaxTime);
873 visibleMetricsIndex.push_back(MetricIndex{si, di, mi, gmi});
874 }
875 gmi++;
876 }
877 if (deviceVisible) {
878 visibleDevicesIndex.push_back(di);
879 }
880 }
881 }
882 if (visibleMetricsIndex.empty()) {
883 TextCenter("Please enable some metric.");
884 ImGui::End();
885 return;
886 };
887
888 switch (currentStyle) {
889 case MetricsDisplayStyle::Stems:
890 case MetricsDisplayStyle::Histos:
891 case MetricsDisplayStyle::Lines: {
892 displayDeviceMetrics("Metrics",
893 minTime, maxTime, 1024,
894 currentStyle, metricDisplayState, metricsStore, driverInfo);
895 } break;
896 case MetricsDisplayStyle::Sparks: {
897 displaySparks(state.startTime, visibleMetricsIndex, metricDisplayState, metricsStore);
898 } break;
899 case MetricsDisplayStyle::Table: {
900 static std::vector<float> visibleDevicesOffsets;
901 visibleDevicesOffsets.clear();
902 visibleDevicesOffsets.resize(visibleDevicesIndex.size());
903
904 size_t lastDevice = -1;
905 int visibleDeviceCount = -1;
907 for (auto index : visibleMetricsIndex) {
908 auto& metricsInfos = metricsStore.metrics[index.storeIndex];
909 if (lastDevice != index.deviceIndex) {
910 visibleDeviceCount++;
911 lastDevice = index.deviceIndex;
912 }
913 auto label = metricsInfos[index.deviceIndex].metricLabels[index.metricIndex].label;
914 visibleDevicesOffsets[visibleDeviceCount] += ImGui::CalcTextSize(label, nullptr, true).x;
915 }
916 // The Device name header.
917 if (ImGui::BeginTable("##metrics-table", visibleMetricsIndex.size() + 1, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX, ImVec2{-1, -1})) {
918 ImGui::TableSetupColumn("##close button", ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_WidthFixed, 20);
919 for (int i = 0; i < visibleMetricsIndex.size(); ++i) {
920 ImGui::TableSetupColumn("##device-header", ImGuiTableColumnFlags_WidthFixed, 100);
921 }
922 ImGui::TableSetupScrollFreeze(1, 2);
923 ImGui::TableNextRow();
924 ImGui::TableNextColumn();
925 ImGui::TextUnformatted("");
926 visibleDeviceCount = -1;
927 lastDevice = -1;
928 for (auto index : visibleMetricsIndex) {
929 ImGui::TableNextColumn();
930 auto& devices = metricsStore.specs[index.storeIndex];
931 if (lastDevice == index.deviceIndex) {
932 continue;
933 }
934 visibleDeviceCount++;
935 lastDevice = index.deviceIndex;
936 auto& spec = devices[index.deviceIndex];
937
938 ImGui::TextUnformatted(spec.label.c_str());
939 }
940
941 // The metrics headers
942 ImGui::TableNextRow();
943 ImGui::TableNextColumn();
944 ImGui::TextUnformatted("#");
945 lastDevice = -1;
946 for (auto index : visibleMetricsIndex) {
947 ImGui::TableNextColumn();
948 auto& metricsInfos = metricsStore.metrics[index.storeIndex];
949 auto& metric = metricsInfos[index.deviceIndex];
950 auto label = metricsInfos[index.deviceIndex].metricLabels[index.metricIndex].label;
951 ImGui::Text("%s (%" PRIu64 ")", label, (uint64_t)metric.metrics[index.metricIndex].filledMetrics);
952 }
953
954 // Calculate which columns we want to see.
955 ImGuiListClipper clipper;
956 // For now
957 clipper.Begin(1024);
958
959 while (clipper.Step()) {
960 for (size_t i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i) {
961 ImGui::TableNextRow();
962 metricsTableRow(visibleMetricsIndex, metricsStore, i);
963 }
964 }
965 ImGui::EndTable();
966 }
967 } break;
968 }
969 ImGui::End();
970}
971
973{
975 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.);
976 if (info.active == false) {
977 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::DARK_RED);
978 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED);
979 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::RED);
980 return;
981 }
982 switch (info.maxLogLevel) {
983 case LogLevel::Critical:
984 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_RED);
985 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED);
986 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_RED);
987 break;
988 case LogLevel::Error:
989 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_RED);
990 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::RED);
991 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_RED);
992 break;
993 case LogLevel::Alarm:
994 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_YELLOW);
995 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::YELLOW);
996 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_YELLOW);
997 break;
998 case LogLevel::Warning:
999 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_YELLOW);
1000 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::YELLOW);
1001 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_YELLOW);
1002 break;
1003 case LogLevel::Info:
1004 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_GREEN);
1005 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::GREEN);
1006 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_GREEN);
1007 break;
1008 default:
1009 ImGui::PushStyleColor(ImGuiCol_TitleBg, PaletteHelpers::SHADED_BLUE);
1010 ImGui::PushStyleColor(ImGuiCol_TitleBgActive, PaletteHelpers::BLUE);
1011 ImGui::PushStyleColor(ImGuiCol_TitleBgCollapsed, PaletteHelpers::SHADED_BLUE);
1012 break;
1013 }
1014}
1015
1017{
1018 ImGui::PopStyleColor(3);
1019 ImGui::PopStyleVar(1);
1020}
1021
1024void displayDriverInfo(DriverInfo const& driverInfo, DriverControl& driverControl)
1025{
1026 ImGui::Begin("Driver information");
1027 static int pid = getpid();
1028
1029 if (driverControl.state == DriverControlState::STEP) {
1030 driverControl.state = DriverControlState::PAUSE;
1031 }
1032 auto state = reinterpret_cast<int*>(&driverControl.state);
1033 ImGui::RadioButton("Play", state, static_cast<int>(DriverControlState::PLAY));
1034 ImGui::SameLine();
1035 ImGui::RadioButton("Pause", state, static_cast<int>(DriverControlState::PAUSE));
1036 ImGui::SameLine();
1037 ImGui::RadioButton("Step", state, static_cast<int>(DriverControlState::STEP));
1038
1039 ImGui::Columns();
1040
1041 ImGui::Text("PID: %d - Control port %d", pid, driverInfo.port);
1042 ImGui::Text("Frame cost (latency): %.1f(%.1f)ms", driverInfo.frameCost, driverInfo.frameLatency);
1043 ImGui::Text("State stack (depth %lu)", driverInfo.states.size());
1044 if (ImGui::Button("SIGCONT all children")) {
1045 kill(0, SIGCONT);
1046 }
1047 ImGui::SameLine();
1048 if (ImGui::Button("Debug driver")) {
1049 std::string pidStr = std::to_string(pid);
1050 setenv("O2DEBUGGEDPID", pidStr.c_str(), 1);
1051#ifdef __APPLE__
1052 std::string defaultAppleDebugCommand =
1053 "osascript -e 'tell application \"Terminal\"'"
1054 " -e 'activate'"
1055 " -e 'do script \"lldb -p \" & (system attribute \"O2DEBUGGEDPID\") & \"; exit\"'"
1056 " -e 'end tell'";
1057 setenv("O2DPLDEBUG", defaultAppleDebugCommand.c_str(), 0);
1058#else
1059 setenv("O2DPLDEBUG", "xterm -hold -e gdb attach $O2DEBUGGEDPID &", 0);
1060#endif
1061 int retVal = system(getenv("O2DPLDEBUG"));
1062 (void)retVal;
1063 }
1064
1065 ImGui::SameLine();
1066 if (ImGui::Button("Profile")) {
1067 std::string pidStr = std::to_string(pid);
1068 setenv("O2PROFILEDPID", pidStr.c_str(), 1);
1069#ifdef __APPLE__
1070 auto defaultAppleProfileCommand = fmt::format(
1071 "osascript -e 'tell application \"Terminal\"'"
1072 " -e 'activate'"
1073 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
1074 " --instrument os_signpost --time-limit 30s --template Time\\\\ Profiler --attach {0} "
1075 " && open dpl-profile-{0}.trace && exit\"'"
1076 " -e 'end tell'",
1077 pid);
1078 std::cout << defaultAppleProfileCommand << std::endl;
1079 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
1080#else
1081 setenv("O2DPLPROFILE", "xterm -hold -e perf record -a -g -p $O2PROFILEDPID > perf-$O2PROFILEDPID.data &", 0);
1082#endif
1083 int retVal = system(getenv("O2DPLPROFILE"));
1084 (void)retVal;
1085 }
1086
1087 // FIXME: this should really be a combo box
1088#ifdef __APPLE__
1089 if (ImGui::Button("Memory Profile")) {
1090 std::string pidStr = std::to_string(pid);
1091 setenv("O2PROFILEDPID", pidStr.c_str(), 1);
1092 auto defaultAppleProfileCommand = fmt::format(
1093 "osascript -e 'tell application \"Terminal\"'"
1094 " -e 'activate'"
1095 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
1096 " --instrument os_signpost --time-limit 30s --template Allocations --attach {0} "
1097 " && open dpl-profile-{0}.trace && exit\"'"
1098 " -e 'end tell'",
1099 pid);
1100 std::cout << defaultAppleProfileCommand << std::endl;
1101 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
1102 int retVal = system(getenv("O2DPLPROFILE"));
1103 (void)retVal;
1104 }
1105#endif
1106
1107 static bool selectedSignpost = false;
1108 if (ImGui::CollapsingHeader("Signposts", ImGuiTreeNodeFlags_DefaultOpen)) {
1109 if (ImGui::Checkbox("Driver", &selectedSignpost)) {
1110 if (selectedSignpost) {
1111 O2_LOG_ENABLE(driver);
1112 } else {
1113 O2_LOG_DISABLE(driver);
1114 }
1115 }
1116 }
1117
1118 for (size_t i = 0; i < driverInfo.states.size(); ++i) {
1119 ImGui::Text("#%lu: %s", i, DriverInfoHelper::stateToString(driverInfo.states[i]));
1120 }
1121
1122 ImGui::End();
1123}
1124
1125// FIXME: return empty function in case we were not built
1126// with GLFW support.
1128std::function<void(void)> getGUIDebugger(std::vector<DeviceInfo> const& infos,
1129 std::vector<DeviceSpec> const& devices,
1130 std::vector<DataProcessingStates> const& allStates,
1131 std::vector<DataProcessorInfo> const& metadata,
1132 std::vector<DeviceMetricsInfo> const& metricsInfos,
1133 DriverInfo const& driverInfo,
1134 std::vector<DeviceControl>& controls,
1135 DriverControl& driverControl)
1136{
1137 static gui::WorkspaceGUIState globalGUIState;
1138 gui::WorkspaceGUIState& guiState = globalGUIState;
1139 guiState.selectedMetric = -1;
1140 guiState.metricMaxRange = 0UL;
1141 guiState.metricMinRange = -1;
1142 // FIXME: this should probaly have a better mapping between our window state and
1143 guiState.devices.resize(infos.size());
1144 for (size_t i = 0; i < guiState.devices.size(); ++i) {
1145 gui::DeviceGUIState& state = guiState.devices[i];
1146 state.label = devices[i].id + "(" + std::to_string(infos[i].pid) + ")";
1147 }
1148 guiState.bottomPaneSize = 340;
1149 guiState.leftPaneSize = 200;
1150 guiState.rightPaneSize = 300;
1151
1152 // Show all the panes by default.
1153 guiState.bottomPaneVisible = true;
1154 guiState.leftPaneVisible = true;
1155 guiState.rightPaneVisible = true;
1156 timespec now;
1157 clock_gettime(CLOCK_REALTIME, &now);
1158 guiState.startTime = now.tv_sec - ImGui::GetTime();
1159 std::vector<TopologyNodeInfo> deviceNodesInfos;
1160 for (auto& device : devices) {
1161 deviceNodesInfos.push_back(TopologyNodeInfo{device.id});
1162 }
1163 std::vector<TopologyNodeInfo> driverNodesInfos;
1164 driverNodesInfos.push_back(TopologyNodeInfo{"driver"});
1165
1166 return [&infos, &devices, &allStates, &metadata, &controls, &metricsInfos, &driverInfo, &driverControl, deviceNodesInfos, driverNodesInfos]() {
1167 ImGuiStyle& style = ImGui::GetStyle();
1168 style.FrameRounding = 0.;
1169 style.WindowRounding = 0.;
1170 style.Colors[ImGuiCol_WindowBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f);
1171 style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0x1b / 255.f, 0x1b / 255.f, 0x1b / 255.f, 1.00f);
1172
1173 showTopologyNodeGraph(guiState, infos, devices, allStates, metadata, controls, metricsInfos);
1174
1175 AllMetricsStore metricsStore;
1176
1177 metricsStore.metrics[DEVICE_METRICS] = std::span(metricsInfos);
1178 metricsStore.metrics[DRIVER_METRICS] = std::span(&driverInfo.metrics, 1);
1179 metricsStore.specs[DEVICE_METRICS] = std::span(deviceNodesInfos);
1180 metricsStore.specs[DRIVER_METRICS] = std::span(driverNodesInfos);
1181 displayMetrics(guiState, driverInfo, infos, metadata, controls, metricsStore);
1182 displayDriverInfo(driverInfo, driverControl);
1183
1184 int windowPosStepping = (ImGui::GetIO().DisplaySize.y - 500) / guiState.devices.size();
1185
1186 for (size_t i = 0; i < guiState.devices.size(); ++i) {
1187 gui::DeviceGUIState& state = guiState.devices[i];
1188 assert(i < infos.size());
1189 assert(i < devices.size());
1190 const DeviceInfo& info = infos[i];
1191
1192 assert(controls.size() == devices.size());
1193 DeviceControl& control = controls[i];
1194
1196 if (control.logVisible) {
1197 ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x / 3 * 2, i * windowPosStepping), ImGuiCond_Once);
1198 ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x / 3, ImGui::GetIO().DisplaySize.y - 300),
1199 ImGuiCond_Once);
1200 ImGui::Begin(state.label.c_str(), &control.logVisible);
1201
1202 ImGui::InputText("Log filter", control.logFilter, sizeof(control.logFilter));
1203 ImGui::InputText("Log start trigger", control.logStartTrigger, sizeof(control.logStartTrigger));
1204 ImGui::InputText("Log stop trigger", control.logStopTrigger, sizeof(control.logStopTrigger));
1205 ImGui::Checkbox("Stop logging", &control.quiet);
1206 ImGui::SameLine();
1207 if (control.tracingFlags) {
1208 control.logLevel = LogParsingHelpers::LogLevel::Debug;
1209 }
1210 ImGui::Combo("Log level", reinterpret_cast<int*>(&control.logLevel), LogParsingHelpers::LOG_LEVELS,
1211 (int)LogParsingHelpers::LogLevel::Size, 5);
1212
1213 ImGui::Separator();
1214 ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetTextLineHeightWithSpacing()), false,
1215 ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoMove);
1216 displayHistory(info, control);
1217 ImGui::EndChild();
1218 ImGui::End();
1219 }
1221 }
1222 };
1223}
1224
1225void updateMousePos(float x, float y)
1226{
1227 ImGuiIO& io = ImGui::GetIO();
1228 io.AddMousePosEvent(x, y);
1229}
1230
1231void updateMouseButton(bool clicked)
1232{
1233 ImGuiIO& io = ImGui::GetIO();
1234 io.AddMouseButtonEvent(0, clicked);
1235}
1236
1237void updateMouseWheel(int direction)
1238{
1239 ImGuiIO& io = ImGui::GetIO();
1240 if (direction > 0) {
1241 io.AddMouseWheelEvent(0, 1.0);
1242 } else {
1243 io.AddMouseWheelEvent(0, -1.0);
1244 }
1245}
1246
1247void updateWindowSize(int x, int y)
1248{
1249 ImGuiIO& io = ImGui::GetIO();
1250 io.DisplaySize = ImVec2(x, y);
1251}
1252
1253void keyEvent(char key, bool down)
1254{
1255 ImGuiIO& io = ImGui::GetIO();
1256 switch (key) {
1257 case 0:
1258 io.AddKeyEvent(ImGuiKey_Tab, down);
1259 break;
1260 case 1:
1261 io.AddKeyEvent(ImGuiKey_LeftArrow, down);
1262 break;
1263 case 2:
1264 io.AddKeyEvent(ImGuiKey_RightArrow, down);
1265 break;
1266 case 3:
1267 io.AddKeyEvent(ImGuiKey_UpArrow, down);
1268 break;
1269 case 4:
1270 io.AddKeyEvent(ImGuiKey_DownArrow, down);
1271 break;
1272 case 5:
1273 io.AddKeyEvent(ImGuiKey_PageUp, down);
1274 break;
1275 case 6:
1276 io.AddKeyEvent(ImGuiKey_PageDown, down);
1277 break;
1278 case 7:
1279 io.AddKeyEvent(ImGuiKey_Home, down);
1280 break;
1281 case 8:
1282 io.AddKeyEvent(ImGuiKey_End, down);
1283 break;
1284 case 10:
1285 io.AddKeyEvent(ImGuiKey_Delete, down);
1286 break;
1287 case 11:
1288 io.AddKeyEvent(ImGuiKey_Backspace, down);
1289 break;
1290 case 13:
1291 io.AddKeyEvent(ImGuiKey_Enter, down);
1292 break;
1293 case 14:
1294 io.AddKeyEvent(ImGuiKey_Escape, down);
1295 break;
1296 default:
1297 io.AddKeyEvent((ImGuiKey)key, down);
1298 }
1299}
1300
1301void charIn(char key)
1302{
1303 ImGuiIO& io = ImGui::GetIO();
1304 io.AddInputCharacter((unsigned short)key);
1305}
1306
1307} // namespace o2::framework::gui
benchmark::State & state
const auto bins
Definition PID.cxx:49
int16_t time
Definition RawEventData.h:4
int32_t i
int32_t retVal
uint16_t pos
Definition RawData.h:3
uint32_t j
Definition RawData.h:0
uint16_t pid
Definition RawData.h:2
#define O2_DECLARE_DYNAMIC_LOG(name)
Definition Signpost.h:483
#define O2_LOG_DISABLE(log)
Definition Signpost.h:489
#define O2_LOG_ENABLE(log)
Definition Signpost.h:488
StringRef key
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizeiptr size
Definition glcorearb.h:659
GLuint index
Definition glcorearb.h:781
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
Definition glcorearb.h:5500
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
State for the main GUI window.
void displayMetrics(gui::WorkspaceGUIState &state, DriverInfo const &driverInfo, std::vector< DeviceInfo > const &infos, std::vector< DataProcessorInfo > const &metadata, std::vector< DeviceControl > &controls, AllMetricsStore const &metricsStore)
void displayDeviceMetrics(const char *label, size_t rangeBegin, size_t rangeEnd, size_t bins, MetricsDisplayStyle displayType, std::vector< MetricDisplayState > &state, AllMetricsStore const &metricStore, DriverInfo const &driverInfo)
void showTopologyNodeGraph(WorkspaceGUIState &state, std::vector< DeviceInfo > const &infos, std::vector< DeviceSpec > const &specs, std::vector< DataProcessingStates > const &allStates, std::vector< DataProcessorInfo > const &metadata, std::vector< DeviceControl > &controls, std::vector< DeviceMetricsInfo > const &metricsInfos)
int formatSI(double value, char *buff, int size, void *user_data)
bool hasAll(const char *s, const char *q)
void updateMouseButton(bool clicked)
void keyEvent(char key, bool down)
void displayHistory(const DeviceInfo &info, DeviceControl &control)
std::function< void(void)> getGUIDebugger(std::vector< DeviceInfo > const &infos, std::vector< DeviceSpec > const &devices, std::vector< DataProcessingStates > const &allStates, std::vector< DataProcessorInfo > const &metadata, std::vector< DeviceMetricsInfo > const &metricsInfos, DriverInfo const &driverInfo, std::vector< DeviceControl > &controls, DriverControl &driverControl)
Helper to get the callback to draw the debug GUI.
void displaySparks(double startTime, std::vector< MetricIndex > &visibleMetricsIndex, std::vector< MetricDisplayState > &metricDisplayStates, AllMetricsStore const &metricStore)
void updateMousePos(float x, float y)
void updateWindowSize(int x, int y)
void updateMouseWheel(int direction)
void TextCenter(char const *text)
void displayDriverInfo(DriverInfo const &driverInfo, DriverControl &driverControl)
int formatTimeSinceStart(double value, char *buff, int size, void *user_data)
void pushWindowColorDueToStatus(const DeviceInfo &info)
ImVec4 colorForLogLevel(LogParsingHelpers::LogLevel logLevel)
void metricsTableRow(std::vector< MetricIndex > metricIndex, AllMetricsStore const &metricsStore, int row)
constexpr size_t metricStorageSize()
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
bool logVisible
wether the log window should be opened.
char logStartTrigger[MAX_USER_FILTER_SIZE]
Start printing log with the last occurence of this.
char logFilter[MAX_USER_FILTER_SIZE]
Lines in the log should match this to be displayed.
char logStopTrigger[MAX_USER_FILTER_SIZE]
Stop producing log with the first occurrence of this after the start.
int tracingFlags
What kind of events should run with the TRACE level.
LogParsingHelpers::LogLevel logLevel
Minimum log level for messages to appear.
bool quiet
wether we should be capturing device output.
std::vector< std::string > history
Definition DeviceInfo.h:56
bool active
Whether the device is active (running) or not.
Definition DeviceInfo.h:65
std::vector< LogParsingHelpers::LogLevel > historyLevel
Definition DeviceInfo.h:59
LogParsingHelpers::LogLevel maxLogLevel
The maximum log level ever seen by this device.
Definition DeviceInfo.h:47
size_t historyPos
The position inside the history circular buffer of this device.
Definition DeviceInfo.h:43
DriverControlState state
Current state of the state machine player.
LogLevel
Possible log levels for device log entries.
std::span< TopologyNodeInfo const > specs[TOTAL_TYPES_OF_METRICS]
std::span< DeviceMetricsInfo const > metrics[TOTAL_TYPES_OF_METRICS]
State for the Device specific inspector.
Information associated to a node in the topology.
std::vector< DeviceGUIState > devices
std::vector< int > row