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
82template <typename Routes>
84{
85 // Find the state spec associated to data_queries
86 auto& view = states.statesViews[(int)id];
87 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
88 std::string_view inputs(states.statesBuffer.data() + view.first, view.size);
89 auto beginInputs = inputs.begin();
90 auto endInputs = beginInputs + view.size;
91 char const* input = beginInputs;
92 size_t i = 0;
93 // Iterate on the ; delimited string_views inside inputs
94 while (input != endInputs) {
95 auto end = std::find(input, endInputs, ';');
96 if ((end - input) == 0) {
97 continue;
98 }
99 ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(routes[i].matcher.lifetime));
100 if (ImGui::IsItemHovered()) {
101 ImGui::BeginTooltip();
102 ImGui::Text("%zu: %.*s (%s)", i, int(end - input), input, InspectorHelpers::getLifeTimeStr(routes[i].matcher.lifetime));
103 ImGui::EndTooltip();
104 }
105 input = end + 1;
106 ++i;
107 }
108 }
109}
110
111void configurationTable(boost::property_tree::ptree const& currentConfig,
112 boost::property_tree::ptree const& currentProvenance)
113{
114 if (currentConfig.empty()) {
115 return;
116 }
117 if (ImGui::CollapsingHeader("Current Config", ImGuiTreeNodeFlags_DefaultOpen)) {
118 ImGui::Columns(2);
119 auto labels = {"Name", "Value"};
120 for (auto& label : labels) {
121 ImGui::TextUnformatted(label);
122 ImGui::NextColumn();
123 }
124 for (auto& option : currentConfig) {
125 ImGui::TextUnformatted(option.first.c_str());
126 if (ImGui::IsItemHovered()) {
127 ImGui::BeginTooltip();
128 ImGui::TextUnformatted(option.first.c_str());
129 ImGui::EndTooltip();
130 }
131 ImGui::NextColumn();
132 ImGui::Text("%s (%s)", option.second.data().c_str(), currentProvenance.get<std::string>(option.first).c_str());
133 if (ImGui::IsItemHovered()) {
134 ImGui::BeginTooltip();
135 ImGui::Text("%s (%s)", option.second.data().c_str(), currentProvenance.get<std::string>(option.first).c_str());
136 ImGui::EndTooltip();
137 }
138 ImGui::NextColumn();
139 }
140 ImGui::Columns(1);
141 }
142}
143
144void optionsTable(const char* label, std::vector<ConfigParamSpec> const& options, const DeviceControl& control)
145{
146 if (options.empty()) {
147 return;
148 }
149 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
150 ImGui::Columns(2);
151 auto labels = {"Name", "Value"};
152 for (auto& label : labels) {
153 ImGui::TextUnformatted(label);
154 ImGui::NextColumn();
155 }
156 for (auto& option : options) {
157 ImGui::TextUnformatted(option.name.c_str());
158 ImGui::NextColumn();
159 auto currentValueIt = control.options.find(option.name);
160
161 // Did not find the option
162 if (currentValueIt == control.options.end()) {
163 switch (option.type) {
165 ImGui::Text(R"("%s" (default))", option.defaultValue.get<const char*>());
166 break;
167 case VariantType::Int:
168 ImGui::Text("%d (default)", option.defaultValue.get<int>());
169 break;
171 ImGui::Text("%d (default)", option.defaultValue.get<int8_t>());
172 break;
174 ImGui::Text("%d (default)", option.defaultValue.get<int16_t>());
175 break;
177 ImGui::Text("%" PRId64 " (default)", option.defaultValue.get<int64_t>());
178 break;
180 ImGui::Text("%d (default)", option.defaultValue.get<uint8_t>());
181 break;
183 ImGui::Text("%d (default)", option.defaultValue.get<uint16_t>());
184 break;
186 ImGui::Text("%d (default)", option.defaultValue.get<uint32_t>());
187 break;
189 ImGui::Text("%" PRIu64 " (default)", option.defaultValue.get<uint64_t>());
190 break;
192 ImGui::Text("%f (default)", option.defaultValue.get<float>());
193 break;
195 ImGui::Text("%f (default)", option.defaultValue.get<double>());
196 break;
198 ImGui::TextUnformatted(""); // no default value
199 break;
200 default:
201 ImGui::TextUnformatted("unknown");
202 }
203 } else {
204 ImGui::TextUnformatted(currentValueIt->second.c_str());
205 }
206 ImGui::NextColumn();
207 }
208 }
209
210 ImGui::Columns(1);
211}
212
213void servicesTable(const char* label, std::vector<ServiceSpec> const& services)
214{
215 if (services.empty()) {
216 return;
217 }
218 if (ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen)) {
219 ImGui::Columns(2);
220 auto labels = {"Service", "Kind"};
221 for (auto& label : labels) {
222 ImGui::TextUnformatted(label);
223 ImGui::NextColumn();
224 }
225 for (auto& service : services) {
226 if (!service.name.empty()) {
227 ImGui::TextUnformatted(service.name.c_str());
228 } else {
229 ImGui::TextUnformatted("unknown");
230 }
231 ImGui::NextColumn();
232 switch (service.kind) {
234 ImGui::TextUnformatted("Serial");
235 break;
237 ImGui::TextUnformatted("Global");
238 break;
240 ImGui::TextUnformatted("Stream");
241 break;
242 default:
243 ImGui::TextUnformatted("unknown");
244 }
245 ImGui::NextColumn();
246 }
247 }
248
249 ImGui::Columns(1);
250}
251
253 DeviceInfo const& info,
256 DataProcessorInfo const& metadata,
257 DeviceControl& control)
258{
259 ImGui::Text("Name: %s", spec.name.c_str());
260 ImGui::Text("Executable: %s", metadata.executable.c_str());
261 if (info.active) {
262 ImGui::Text("Pid: %d", info.pid);
263 } else {
264 ImGui::Text("Pid: %d (exit status: %d)", info.pid, info.exitStatus);
265 }
266 ImGui::Text("Device state: %s", info.deviceState.data());
267 ImGui::Text("Rank: %zu/%zu%%%zu/%zu", spec.rank, spec.nSlots, spec.inputTimesliceId, spec.maxInputTimeslices);
268
269 if (ImGui::Button(ICON_FA_BUG "Attach debugger")) {
270 std::string pid = std::to_string(info.pid);
271 setenv("O2DEBUGGEDPID", pid.c_str(), 1);
272#ifdef __APPLE__
273 std::string defaultAppleDebugCommand =
274 "osascript -e 'tell application \"Terminal\" to activate'"
275 " -e 'tell application \"Terminal\" to do script \"lldb -p \" & (system attribute \"O2DEBUGGEDPID\") & \"; exit\"'";
276 setenv("O2DPLDEBUG", defaultAppleDebugCommand.c_str(), 0);
277#else
278 setenv("O2DPLDEBUG", "xterm -hold -e gdb attach $O2DEBUGGEDPID &", 0);
279#endif
280 int retVal = system(getenv("O2DPLDEBUG"));
281 (void)retVal;
282 }
283
284 ImGui::SameLine();
285 if (ImGui::Button("Profile 30s")) {
286 std::string pid = std::to_string(info.pid);
287 setenv("O2PROFILEDPID", pid.c_str(), 1);
288#ifdef __APPLE__
289 auto defaultAppleProfileCommand = fmt::format(
290 "osascript -e 'tell application \"Terminal\"'"
291 " -e 'activate'"
292 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
293 " --instrument os_signpost --time-limit 30s --template Time\\\\ Profiler --attach {0} "
294 " && open dpl-profile-{0}.trace && exit\"'"
295 " -e 'end tell'",
296 pid);
297
298 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
299#else
300 setenv("O2DPLPROFILE", "xterm -hold -e perf record -a -g -p $O2PROFILEDPID > perf-$O2PROFILEDPID.data &", 0);
301#endif
302 LOG(error) << getenv("O2DPLPROFILE");
303 int retVal = system(getenv("O2DPLPROFILE"));
304 (void)retVal;
305 }
306
307#ifdef __APPLE__
308 if (ImGui::Button("Profile Allocations 30s")) {
309 std::string pid = std::to_string(info.pid);
310 setenv("O2PROFILEDPID", pid.c_str(), 1);
311 auto defaultAppleProfileCommand = fmt::format(
312 "osascript -e 'tell application \"Terminal\"'"
313 " -e 'activate'"
314 " -e 'do script \"xcrun xctrace record --output dpl-profile-{0}.trace"
315 " --time-limit 30s --instrument os_signpost --template Allocations --attach {0} "
316 " && open dpl-profile-{0}.trace && exit\"'"
317 " -e 'end tell'",
318 pid);
319
320 setenv("O2DPLPROFILE", defaultAppleProfileCommand.c_str(), 0);
321 LOG(error) << getenv("O2DPLPROFILE");
322 int retVal = system(getenv("O2DPLPROFILE"));
323 (void)retVal;
324 }
325#endif
326
327 if (control.controller) {
328 if (ImGui::Button("Offer SHM")) {
329 control.controller->write("/shm-offer 1000", strlen("/shm-offer 1000"));
330 }
331
332 if (control.requestedState > info.providedState) {
333 ImGui::Text(ICON_FA_CLOCK_O);
334 } else {
335 if (ImGui::Button("Restart")) {
336 control.requestedState = info.providedState + 1;
337 control.controller->write("/restart", strlen("/restart"));
338 }
339 }
340 }
341
346 optionsTable("Workflow Options", metadata.workflowOptions, control);
347 if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen)) {
348 for (auto& label : spec.labels) {
349 ImGui::Text("%s", label.value.c_str());
350 }
351 }
352 servicesTable("Services", spec.services);
353 if (ImGui::CollapsingHeader("Command line arguments", ImGuiTreeNodeFlags_DefaultOpen)) {
354 static ImGuiTextFilter filter;
355 filter.Draw(ICON_FA_SEARCH);
356 for (auto& arg : metadata.cmdLineArgs) {
357 if (filter.PassFilter(arg.c_str())) {
358 ImGui::TextUnformatted(arg.c_str());
359 }
360 }
361 }
362
363 if (ImGui::CollapsingHeader("Channels", ImGuiTreeNodeFlags_DefaultOpen)) {
364 ImGui::Text("# channels: %lu", spec.inputChannels.size() + spec.outputChannels.size());
367 }
368 if (ImGui::CollapsingHeader("Policies")) {
369 ImGui::Text("Completion: %s", spec.completionPolicy.name.c_str());
370 ImGui::Text("Sending: %s", spec.sendingPolicy.name.c_str());
371 ImGui::Text("Dispatching: %s", spec.dispatchPolicy.name.c_str());
372 }
373
374 if (ImGui::CollapsingHeader("Signals", ImGuiTreeNodeFlags_DefaultOpen)) {
375 if (ImGui::Button("SIGSTOP")) {
376 kill(info.pid, SIGSTOP);
377 }
378 ImGui::SameLine();
379 if (ImGui::Button("SIGTERM")) {
380 kill(info.pid, SIGTERM);
381 }
382 ImGui::SameLine();
383 if (ImGui::Button("SIGKILL")) {
384 kill(info.pid, SIGKILL);
385 }
386 if (ImGui::Button("SIGCONT")) {
387 kill(info.pid, SIGCONT);
388 }
389 ImGui::SameLine();
390 if (ImGui::Button("SIGUSR1")) {
391 kill(info.pid, SIGUSR1);
392 }
393 ImGui::SameLine();
394 if (ImGui::Button("SIGUSR2")) {
395 kill(info.pid, SIGUSR2);
396 }
397 }
398
399 bool logsChanged = false;
400 if (ImGui::CollapsingHeader("Signposts", ImGuiTreeNodeFlags_DefaultOpen)) {
401 logsChanged = ImGui::CheckboxFlags("Device", &control.logStreams, DeviceState::LogStreams::DEVICE_LOG);
402 logsChanged = ImGui::CheckboxFlags("Completion", &control.logStreams, DeviceState::LogStreams::COMPLETION_LOG);
403 logsChanged = ImGui::CheckboxFlags("Monitoring", &control.logStreams, DeviceState::LogStreams::MONITORING_SERVICE_LOG);
404 logsChanged = ImGui::CheckboxFlags("DataProcessorContext", &control.logStreams, DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG);
405 logsChanged = ImGui::CheckboxFlags("StreamContext", &control.logStreams, DeviceState::LogStreams::STREAM_CONTEXT_LOG);
406 if (logsChanged && control.controller) {
407 std::string cmd = fmt::format("/log-streams {}", control.logStreams);
408 control.controller->write(cmd.c_str(), cmd.size());
409 }
410 }
411
412 bool flagsChanged = false;
413 if (ImGui::CollapsingHeader("Event loop tracing", ImGuiTreeNodeFlags_DefaultOpen)) {
414 flagsChanged |= ImGui::CheckboxFlags("METRICS_MUST_FLUSH", &control.tracingFlags, DeviceState::LoopReason::METRICS_MUST_FLUSH);
415 flagsChanged |= ImGui::CheckboxFlags("SIGNAL_ARRIVED", &control.tracingFlags, DeviceState::LoopReason::SIGNAL_ARRIVED);
416 flagsChanged |= ImGui::CheckboxFlags("DATA_SOCKET_POLLED", &control.tracingFlags, DeviceState::LoopReason::DATA_SOCKET_POLLED);
417 flagsChanged |= ImGui::CheckboxFlags("DATA_INCOMING", &control.tracingFlags, DeviceState::LoopReason::DATA_INCOMING);
418 flagsChanged |= ImGui::CheckboxFlags("DATA_OUTGOING", &control.tracingFlags, DeviceState::LoopReason::DATA_OUTGOING);
419 flagsChanged |= ImGui::CheckboxFlags("WS_COMMUNICATION", &control.tracingFlags, DeviceState::LoopReason::WS_COMMUNICATION);
420 flagsChanged |= ImGui::CheckboxFlags("TIMER_EXPIRED", &control.tracingFlags, DeviceState::LoopReason::TIMER_EXPIRED);
421 flagsChanged |= ImGui::CheckboxFlags("WS_CONNECTED", &control.tracingFlags, DeviceState::LoopReason::WS_CONNECTED);
422 flagsChanged |= ImGui::CheckboxFlags("WS_CLOSING", &control.tracingFlags, DeviceState::LoopReason::WS_CLOSING);
423 flagsChanged |= ImGui::CheckboxFlags("WS_READING", &control.tracingFlags, DeviceState::LoopReason::WS_READING);
424 flagsChanged |= ImGui::CheckboxFlags("WS_WRITING", &control.tracingFlags, DeviceState::LoopReason::WS_WRITING);
425 flagsChanged |= ImGui::CheckboxFlags("ASYNC_NOTIFICATION", &control.tracingFlags, DeviceState::LoopReason::ASYNC_NOTIFICATION);
426 flagsChanged |= ImGui::CheckboxFlags("OOB_ACTIVITY", &control.tracingFlags, DeviceState::LoopReason::OOB_ACTIVITY);
427 flagsChanged |= ImGui::CheckboxFlags("UNKNOWN", &control.tracingFlags, DeviceState::LoopReason::UNKNOWN);
428 flagsChanged |= ImGui::CheckboxFlags("FIRST_LOOP", &control.tracingFlags, DeviceState::LoopReason::FIRST_LOOP);
429 flagsChanged |= ImGui::CheckboxFlags("NEW_STATE_PENDING", &control.tracingFlags, DeviceState::LoopReason::NEW_STATE_PENDING);
430 flagsChanged |= ImGui::CheckboxFlags("PREVIOUSLY_ACTIVE", &control.tracingFlags, DeviceState::LoopReason::PREVIOUSLY_ACTIVE);
431 flagsChanged |= ImGui::CheckboxFlags("TRACE_CALLBACKS", &control.tracingFlags, DeviceState::LoopReason::TRACE_CALLBACKS);
432 flagsChanged |= ImGui::CheckboxFlags("TRACE_USERCODE", &control.tracingFlags, DeviceState::LoopReason::TRACE_USERCODE);
433 if (flagsChanged && control.controller) {
434 std::string cmd = fmt::format("/trace {}", control.tracingFlags);
435 control.controller->write(cmd.c_str(), cmd.size());
436 }
437 }
438}
439
440} // namespace o2::framework::gui
std::vector< OutputRoute > routes
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 deviceInfoTable(char const *label, ProcessingStateId id, DataProcessingStates const &states, Routes const &routes, DeviceMetricsInfo const &metrics)
void configurationTable(boost::property_tree::ptree const &currentConfig, boost::property_tree::ptree const &currentProvenance)
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)
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 char * getLifeTimeStr(Lifetime lifetime)
static void channelsTable(const char *title, const C &channels)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< ChannelData > channels