Project
Loading...
Searching...
No Matches
FrameworkGUIDeviceInspector.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.
11
14
20#include "Framework/Logger.h"
23#include "Framework/Signpost.h"
24#include "InspectorHelpers.h"
25#include <DebugGUI/icons_font_awesome.h>
26
27#include "DebugGUI/imgui.h"
28#include <cinttypes>
29#include <csignal>
30#include <cstdlib>
31#include <iostream>
32
33namespace o2::framework::gui
34{
35
37 template <typename C>
38 static void channelsTable(const char* title, const C& channels)
39 {
40 ImGui::TextUnformatted(title);
41 ImGui::Columns(2);
42 ImGui::TextUnformatted("Name");
43 ImGui::NextColumn();
44 ImGui::TextUnformatted("Port");
45 ImGui::NextColumn();
46 for (auto channel : channels) {
47 ImGui::TextUnformatted(channel.name.c_str());
48 ImGui::NextColumn();
49 ImGui::Text("%d", channel.port);
50 ImGui::NextColumn();
51 }
52 ImGui::Columns(1);
53 }
54};
55
57{
58 static std::vector<char> statesActive(states.statesViews.size(), false);
59 if (statesActive.size() != states.statesViews.size()) {
60 statesActive.resize(states.statesViews.size(), false);
61 }
62
63 if (ImGui::CollapsingHeader("Remote state", ImGuiTreeNodeFlags_DefaultOpen)) {
64 for (size_t i = 0; i < states.stateNames.size(); ++i) {
65 if (states.stateNames[i].empty()) {
66 continue;
67 }
68 auto& view = states.statesViews[i];
69 if (view.size == 0) {
70 continue;
71 }
72 ImGui::Checkbox(states.stateNames[i].c_str(), (bool*)&statesActive[i]);
73 if (view.size && statesActive[i] != 0) {
74 ImGui::Begin(states.stateNames[i].c_str());
75 ImGui::Text("%d-%d\n %.*s", view.first, view.first + view.size, view.size, states.statesBuffer.data() + view.first);
76 ImGui::End();
77 }
78 }
79 }
80}
81
82void deviceInfoTable(char const* label, ProcessingStateId id, DataProcessingStates const& states, std::variant<std::vector<InputRoute>, std::vector<OutputRoute>> routes, DeviceMetricsInfo const& metrics)
83{
84 // Find the state spec associated to data_queries
85 auto& view = states.statesViews[(int)id];
86 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
87 std::string_view inputs(states.statesBuffer.data() + view.first, view.size);
88 auto beginInputs = inputs.begin();
89 auto endInputs = beginInputs + view.size;
90 char const* input = beginInputs;
91 size_t i = 0;
92 // Iterate on the ; delimited string_views inside inputs
93 while (input != endInputs) {
94 auto end = std::find(input, endInputs, ';');
95 if ((end - input) == 0) {
96 continue;
97 }
98 auto getLifetime = [&routes, &i]() -> Lifetime {
99 if (std::get_if<std::vector<InputRoute>>(&routes)) {
100 return std::get<std::vector<InputRoute>>(routes)[i].matcher.lifetime;
101 } else {
102 return std::get<std::vector<OutputRoute>>(routes)[i].matcher.lifetime;
103 }
104 };
105 ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(getLifetime()).c_str());
106 if (ImGui::IsItemHovered()) {
107 ImGui::BeginTooltip();
108 ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(getLifetime()).c_str());
109 ImGui::EndTooltip();
110 }
111 input = end + 1;
112 ++i;
113 }
114 }
115}
116
117void configurationTable(boost::property_tree::ptree const& currentConfig,
118 boost::property_tree::ptree const& currentProvenance)
119{
120 if (currentConfig.empty()) {
121 return;
122 }
123 if (ImGui::CollapsingHeader("Current Config", ImGuiTreeNodeFlags_DefaultOpen)) {
124 ImGui::Columns(2);
125 auto labels = {"Name", "Value"};
126 for (auto& label : labels) {
127 ImGui::TextUnformatted(label);
128 ImGui::NextColumn();
129 }
130 for (auto& option : currentConfig) {
131 ImGui::TextUnformatted(option.first.c_str());
132 if (ImGui::IsItemHovered()) {
133 ImGui::BeginTooltip();
134 ImGui::TextUnformatted(option.first.c_str());
135 ImGui::EndTooltip();
136 }
137 ImGui::NextColumn();
138 ImGui::Text("%s (%s)", option.second.data().c_str(), currentProvenance.get<std::string>(option.first).c_str());
139 if (ImGui::IsItemHovered()) {
140 ImGui::BeginTooltip();
141 ImGui::Text("%s (%s)", option.second.data().c_str(), currentProvenance.get<std::string>(option.first).c_str());
142 ImGui::EndTooltip();
143 }
144 ImGui::NextColumn();
145 }
146 ImGui::Columns(1);
147 }
148}
149
150void optionsTable(const char* label, std::vector<ConfigParamSpec> const& options, const DeviceControl& control)
151{
152 if (options.empty()) {
153 return;
154 }
155 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
156 ImGui::Columns(2);
157 auto labels = {"Name", "Value"};
158 for (auto& label : labels) {
159 ImGui::TextUnformatted(label);
160 ImGui::NextColumn();
161 }
162 for (auto& option : options) {
163 ImGui::TextUnformatted(option.name.c_str());
164 ImGui::NextColumn();
165 auto currentValueIt = control.options.find(option.name);
166
167 // Did not find the option
168 if (currentValueIt == control.options.end()) {
169 switch (option.type) {
171 ImGui::Text(R"("%s" (default))", option.defaultValue.get<const char*>());
172 break;
173 case VariantType::Int:
174 ImGui::Text("%d (default)", option.defaultValue.get<int>());
175 break;
177 ImGui::Text("%d (default)", option.defaultValue.get<int8_t>());
178 break;
180 ImGui::Text("%d (default)", option.defaultValue.get<int16_t>());
181 break;
183 ImGui::Text("%" PRId64 " (default)", option.defaultValue.get<int64_t>());
184 break;
186 ImGui::Text("%d (default)", option.defaultValue.get<uint8_t>());
187 break;
189 ImGui::Text("%d (default)", option.defaultValue.get<uint16_t>());
190 break;
192 ImGui::Text("%d (default)", option.defaultValue.get<uint32_t>());
193 break;
195 ImGui::Text("%" PRIu64 " (default)", option.defaultValue.get<uint64_t>());
196 break;
198 ImGui::Text("%f (default)", option.defaultValue.get<float>());
199 break;
201 ImGui::Text("%f (default)", option.defaultValue.get<double>());
202 break;
204 ImGui::TextUnformatted(""); // no default value
205 break;
206 default:
207 ImGui::TextUnformatted("unknown");
208 }
209 } else {
210 ImGui::TextUnformatted(currentValueIt->second.c_str());
211 }
212 ImGui::NextColumn();
213 }
214 }
215
216 ImGui::Columns(1);
217}
218
219void servicesTable(const char* label, std::vector<ServiceSpec> const& services)
220{
221 if (services.empty()) {
222 return;
223 }
224 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
225 ImGui::Columns(2);
226 auto labels = {"Service", "Kind"};
227 for (auto& label : labels) {
228 ImGui::TextUnformatted(label);
229 ImGui::NextColumn();
230 }
231 for (auto& service : services) {
232 if (!service.name.empty()) {
233 ImGui::TextUnformatted(service.name.c_str());
234 } else {
235 ImGui::TextUnformatted("unknown");
236 }
237 ImGui::NextColumn();
238 switch (service.kind) {
240 ImGui::TextUnformatted("Serial");
241 break;
243 ImGui::TextUnformatted("Global");
244 break;
246 ImGui::TextUnformatted("Stream");
247 break;
248 default:
249 ImGui::TextUnformatted("unknown");
250 }
251 ImGui::NextColumn();
252 }
253 }
254
255 ImGui::Columns(1);
256}
257
259 DeviceInfo const& info,
262 DataProcessorInfo const& metadata,
263 DeviceControl& control)
264{
265 ImGui::Text("Name: %s", spec.name.c_str());
266 ImGui::Text("Executable: %s", metadata.executable.c_str());
267 if (info.active) {
268 ImGui::Text("Pid: %d", info.pid);
269 } else {
270 ImGui::Text("Pid: %d (exit status: %d)", info.pid, info.exitStatus);
271 }
272 ImGui::Text("Device state: %s", info.deviceState.data());
273 ImGui::Text("Rank: %zu/%zu%%%zu/%zu", spec.rank, spec.nSlots, spec.inputTimesliceId, spec.maxInputTimeslices);
274
275 if (ImGui::Button(ICON_FA_BUG "Attach debugger")) {
276 std::string pid = std::to_string(info.pid);
277 setenv("O2DEBUGGEDPID", pid.c_str(), 1);
278#ifdef __APPLE__
279 std::string defaultAppleDebugCommand =
280 "osascript -e 'tell application \"Terminal\" to activate'"
281 " -e 'tell application \"Terminal\" to do script \"lldb -p \" & (system attribute \"O2DEBUGGEDPID\") & \"; exit\"'";
282 setenv("O2DPLDEBUG", defaultAppleDebugCommand.c_str(), 0);
283#else
284 setenv("O2DPLDEBUG", "xterm -hold -e gdb attach $O2DEBUGGEDPID &", 0);
285#endif
286 int retVal = system(getenv("O2DPLDEBUG"));
287 (void)retVal;
288 }
289
290 ImGui::SameLine();
291 if (ImGui::Button("Profile 30s")) {
292 std::string pid = std::to_string(info.pid);
293 setenv("O2PROFILEDPID", pid.c_str(), 1);
294#ifdef __APPLE__
295 auto defaultAppleProfileCommand = fmt::format(
296 "osascript -e 'tell application \"Terminal\"'"
297 " -e 'activate'"
298 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
299 " --instrument os_signpost --time-limit 30s --template Time\\\\ Profiler --attach {0} "
300 " && open dpl-profile-{0}.trace && exit\"'"
301 " -e 'end tell'",
302 pid);
303
304 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
305#else
306 setenv("O2DPLPROFILE", "xterm -hold -e perf record -a -g -p $O2PROFILEDPID > perf-$O2PROFILEDPID.data &", 0);
307#endif
308 LOG(error) << getenv("O2DPLPROFILE");
309 int retVal = system(getenv("O2DPLPROFILE"));
310 (void)retVal;
311 }
312
313#ifdef __APPLE__
314 if (ImGui::Button("Profile Allocations 30s")) {
315 std::string pid = std::to_string(info.pid);
316 setenv("O2PROFILEDPID", pid.c_str(), 1);
317 auto defaultAppleProfileCommand = fmt::format(
318 "osascript -e 'tell application \"Terminal\"'"
319 " -e 'activate'"
320 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
321 " --time-limit 30s --instrument os_signpost --template Allocations --attach {0} "
322 " && open dpl-profile-{0}.trace && exit\"'"
323 " -e 'end tell'",
324 pid);
325
326 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
327 LOG(error) << getenv("O2DPLPROFILE");
328 int retVal = system(getenv("O2DPLPROFILE"));
329 (void)retVal;
330 }
331#endif
332
333 if (control.controller) {
334 if (ImGui::Button("Offer SHM")) {
335 control.controller->write("/shm-offer 1000", strlen("/shm-offer 1000"));
336 }
337
338 if (control.requestedState > info.providedState) {
339 ImGui::Text(ICON_FA_CLOCK_O);
340 } else {
341 if (ImGui::Button("Restart")) {
342 control.requestedState = info.providedState + 1;
343 control.controller->write("/restart", strlen("/restart"));
344 }
345 }
346 }
347
349 deviceInfoTable("Inputs:", ProcessingStateId::DATA_QUERIES, states, std::variant<std::vector<InputRoute>, std::vector<OutputRoute>>(spec.inputs), metrics);
350 deviceInfoTable("Outputs:", ProcessingStateId::OUTPUT_MATCHERS, states, std::variant<std::vector<InputRoute>, std::vector<OutputRoute>>(spec.outputs), metrics);
352 optionsTable("Workflow Options", metadata.workflowOptions, control);
353 if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen)) {
354 for (auto& label : spec.labels) {
355 ImGui::Text("%s", label.value.c_str());
356 }
357 }
358 servicesTable("Services", spec.services);
359 if (ImGui::CollapsingHeader("Command line arguments", ImGuiTreeNodeFlags_DefaultOpen)) {
360 static ImGuiTextFilter filter;
361 filter.Draw(ICON_FA_SEARCH);
362 for (auto& arg : metadata.cmdLineArgs) {
363 if (filter.PassFilter(arg.c_str())) {
364 ImGui::TextUnformatted(arg.c_str());
365 }
366 }
367 }
368
369 if (ImGui::CollapsingHeader("Channels", ImGuiTreeNodeFlags_DefaultOpen)) {
370 ImGui::Text("# channels: %lu", spec.inputChannels.size() + spec.outputChannels.size());
373 }
374 if (ImGui::CollapsingHeader("Policies")) {
375 ImGui::Text("Completion: %s", spec.completionPolicy.name.c_str());
376 ImGui::Text("Sending: %s", spec.sendingPolicy.name.c_str());
377 ImGui::Text("Dispatching: %s", spec.dispatchPolicy.name.c_str());
378 }
379
380 if (ImGui::CollapsingHeader("Signals", ImGuiTreeNodeFlags_DefaultOpen)) {
381 if (ImGui::Button("SIGSTOP")) {
382 kill(info.pid, SIGSTOP);
383 }
384 ImGui::SameLine();
385 if (ImGui::Button("SIGTERM")) {
386 kill(info.pid, SIGTERM);
387 }
388 ImGui::SameLine();
389 if (ImGui::Button("SIGKILL")) {
390 kill(info.pid, SIGKILL);
391 }
392 if (ImGui::Button("SIGCONT")) {
393 kill(info.pid, SIGCONT);
394 }
395 ImGui::SameLine();
396 if (ImGui::Button("SIGUSR1")) {
397 kill(info.pid, SIGUSR1);
398 }
399 ImGui::SameLine();
400 if (ImGui::Button("SIGUSR2")) {
401 kill(info.pid, SIGUSR2);
402 }
403 }
404
405 bool logsChanged = false;
406 if (ImGui::CollapsingHeader("Signposts", ImGuiTreeNodeFlags_DefaultOpen)) {
407 logsChanged = ImGui::CheckboxFlags("Device", &control.logStreams, DeviceState::LogStreams::DEVICE_LOG);
408 logsChanged = ImGui::CheckboxFlags("Completion", &control.logStreams, DeviceState::LogStreams::COMPLETION_LOG);
409 logsChanged = ImGui::CheckboxFlags("Monitoring", &control.logStreams, DeviceState::LogStreams::MONITORING_SERVICE_LOG);
410 logsChanged = ImGui::CheckboxFlags("DataProcessorContext", &control.logStreams, DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG);
411 logsChanged = ImGui::CheckboxFlags("StreamContext", &control.logStreams, DeviceState::LogStreams::STREAM_CONTEXT_LOG);
412 if (logsChanged && control.controller) {
413 std::string cmd = fmt::format("/log-streams {}", control.logStreams);
414 control.controller->write(cmd.c_str(), cmd.size());
415 }
416 }
417
418 bool flagsChanged = false;
419 if (ImGui::CollapsingHeader("Event loop tracing", ImGuiTreeNodeFlags_DefaultOpen)) {
420 flagsChanged |= ImGui::CheckboxFlags("METRICS_MUST_FLUSH", &control.tracingFlags, DeviceState::LoopReason::METRICS_MUST_FLUSH);
421 flagsChanged |= ImGui::CheckboxFlags("SIGNAL_ARRIVED", &control.tracingFlags, DeviceState::LoopReason::SIGNAL_ARRIVED);
422 flagsChanged |= ImGui::CheckboxFlags("DATA_SOCKET_POLLED", &control.tracingFlags, DeviceState::LoopReason::DATA_SOCKET_POLLED);
423 flagsChanged |= ImGui::CheckboxFlags("DATA_INCOMING", &control.tracingFlags, DeviceState::LoopReason::DATA_INCOMING);
424 flagsChanged |= ImGui::CheckboxFlags("DATA_OUTGOING", &control.tracingFlags, DeviceState::LoopReason::DATA_OUTGOING);
425 flagsChanged |= ImGui::CheckboxFlags("WS_COMMUNICATION", &control.tracingFlags, DeviceState::LoopReason::WS_COMMUNICATION);
426 flagsChanged |= ImGui::CheckboxFlags("TIMER_EXPIRED", &control.tracingFlags, DeviceState::LoopReason::TIMER_EXPIRED);
427 flagsChanged |= ImGui::CheckboxFlags("WS_CONNECTED", &control.tracingFlags, DeviceState::LoopReason::WS_CONNECTED);
428 flagsChanged |= ImGui::CheckboxFlags("WS_CLOSING", &control.tracingFlags, DeviceState::LoopReason::WS_CLOSING);
429 flagsChanged |= ImGui::CheckboxFlags("WS_READING", &control.tracingFlags, DeviceState::LoopReason::WS_READING);
430 flagsChanged |= ImGui::CheckboxFlags("WS_WRITING", &control.tracingFlags, DeviceState::LoopReason::WS_WRITING);
431 flagsChanged |= ImGui::CheckboxFlags("ASYNC_NOTIFICATION", &control.tracingFlags, DeviceState::LoopReason::ASYNC_NOTIFICATION);
432 flagsChanged |= ImGui::CheckboxFlags("OOB_ACTIVITY", &control.tracingFlags, DeviceState::LoopReason::OOB_ACTIVITY);
433 flagsChanged |= ImGui::CheckboxFlags("UNKNOWN", &control.tracingFlags, DeviceState::LoopReason::UNKNOWN);
434 flagsChanged |= ImGui::CheckboxFlags("FIRST_LOOP", &control.tracingFlags, DeviceState::LoopReason::FIRST_LOOP);
435 flagsChanged |= ImGui::CheckboxFlags("NEW_STATE_PENDING", &control.tracingFlags, DeviceState::LoopReason::NEW_STATE_PENDING);
436 flagsChanged |= ImGui::CheckboxFlags("PREVIOUSLY_ACTIVE", &control.tracingFlags, DeviceState::LoopReason::PREVIOUSLY_ACTIVE);
437 flagsChanged |= ImGui::CheckboxFlags("TRACE_CALLBACKS", &control.tracingFlags, DeviceState::LoopReason::TRACE_CALLBACKS);
438 flagsChanged |= ImGui::CheckboxFlags("TRACE_USERCODE", &control.tracingFlags, DeviceState::LoopReason::TRACE_USERCODE);
439 if (flagsChanged && control.controller) {
440 std::string cmd = fmt::format("/trace {}", control.tracingFlags);
441 control.controller->write(cmd.c_str(), cmd.size());
442 }
443 }
444}
445
446} // namespace o2::framework::gui
int32_t i
int32_t retVal
uint16_t pid
Definition RawData.h:2
GLuint GLuint end
Definition glcorearb.h:469
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
Definition glcorearb.h:5500
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLuint * states
Definition glcorearb.h:4932
State for the main GUI window.
void servicesTable(const char *label, std::vector< ServiceSpec > const &services)
void deviceStateTable(DataProcessingStates const &states)
void configurationTable(boost::property_tree::ptree const &currentConfig, boost::property_tree::ptree const &currentProvenance)
void deviceInfoTable(char const *label, ProcessingStateId id, DataProcessingStates const &states, std::variant< std::vector< InputRoute >, std::vector< OutputRoute > > routes, DeviceMetricsInfo const &metrics)
void displayDeviceInspector(DeviceSpec const &spec, DeviceInfo const &info, DataProcessingStates const &states, DeviceMetricsInfo const &metrics, DataProcessorInfo const &metadata, DeviceControl &control)
Helper to display information about a device.
void optionsTable(const char *label, std::vector< ConfigParamSpec > const &options, const DeviceControl &control)
Lifetime
Possible Lifetime of objects being exchanged by the DPL.
Definition Lifetime.h:18
ProcessingStateId
Helper struct to define some known states.
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string name
Name of the policy itself.
std::vector< ConfigParamSpec > workflowOptions
The workflow options which are available for the associated DataProcessorSpec.
std::vector< std::string > cmdLineArgs
The argument passed on the command line for this DataProcessorSpec.
std::string executable
The executable name of the program which holds the DataProcessorSpec.
int logStreams
What kind of log streams should be enabled.
DeviceController * controller
Handler used to communicate with the device (if available)
std::map< std::string, std::string > options
Where the GUI should store the options it wants.
int requestedState
An incremental number to identify the device state.
int tracingFlags
What kind of events should run with the TRACE level.
void write(const char *message, size_t s)
bool active
Whether the device is active (running) or not.
Definition DeviceInfo.h:65
boost::property_tree::ptree currentConfig
Current configuration for the device.
Definition DeviceInfo.h:82
pid_t pid
The pid of the device associated to this device.
Definition DeviceInfo.h:36
boost::property_tree::ptree currentProvenance
Current provenance for the configuration keys.
Definition DeviceInfo.h:84
int providedState
An incremental number for the state of the device.
Definition DeviceInfo.h:88
std::vector< DataProcessorLabel > labels
Definition DeviceSpec.h:81
std::vector< InputRoute > inputs
Definition DeviceSpec.h:62
size_t maxInputTimeslices
The maximum number of time pipelining for this device.
Definition DeviceSpec.h:70
CompletionPolicy completionPolicy
The completion policy to use for this device.
Definition DeviceSpec.h:72
std::vector< OutputChannelSpec > outputChannels
Definition DeviceSpec.h:55
std::string name
The name of the associated DataProcessorSpec.
Definition DeviceSpec.h:50
size_t inputTimesliceId
The time pipelining id of this particular device.
Definition DeviceSpec.h:68
std::vector< OutputRoute > outputs
Definition DeviceSpec.h:63
DispatchPolicy dispatchPolicy
Definition DeviceSpec.h:73
SendingPolicy sendingPolicy
Definition DeviceSpec.h:75
std::vector< InputChannelSpec > inputChannels
Definition DeviceSpec.h:54
std::vector< ServiceSpec > services
Definition DeviceSpec.h:58
std::string name
Name of the policy itself.
static const std::string getLifeTimeStr(Lifetime lifetime)
static void channelsTable(const char *title, const C &channels)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< ChannelData > channels