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
std::unique_ptr< expressions::Node > node
int16_t time
Definition RawEventData.h:4
int32_t i
int32_t retVal
bounded_vector< float > bins
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:489
#define O2_LOG_DISABLE(log)
Definition Signpost.h:495
#define O2_LOG_ENABLE(log)
Definition Signpost.h:494
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.
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