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