libclues
Linux C++ Tracing Library
Loading...
Searching...
No Matches
format.cxx
1// C++
2#include <cctype>
3#include <sstream>
4
5// Linux
6#include <sys/resource.h> // *rlimit()
7#include <sys/time.h>
8
9// cosmos
10#include <cosmos/formatting.hxx>
11#include <cosmos/fs/filesystem.hxx>
12#include <cosmos/proc/ptrace.hxx>
13#include <cosmos/string.hxx>
14#include <cosmos/utils.hxx>
15
16// clues
17#include <clues/format.hxx>
18#include <clues/macros.h>
19#include <clues/SystemCall.hxx>
20#include <clues/utils.hxx>
21#include <clues/private/kernel/sigaction.hxx>
22
23using namespace std::string_literals;
24
25namespace clues::format {
26
27std::string signal(const cosmos::SignalNr signal, const bool verbose) {
28 std::stringstream ss;
29
30 const auto SIGRTMIN_PRIV = SIGRTMIN - 2;
31
32 if (const auto raw = cosmos::to_integral(signal); raw < SIGRTMIN_PRIV || raw > SIGRTMAX) {
33 switch (raw) {
34 CASE_ENUM_TO_STR(SIGINT);
35 CASE_ENUM_TO_STR(SIGTERM);
36 CASE_ENUM_TO_STR(SIGHUP);
37 CASE_ENUM_TO_STR(SIGQUIT);
38 CASE_ENUM_TO_STR(SIGILL);
39 CASE_ENUM_TO_STR(SIGABRT);
40 CASE_ENUM_TO_STR(SIGFPE);
41 CASE_ENUM_TO_STR(SIGKILL);
42 CASE_ENUM_TO_STR(SIGPIPE);
43 CASE_ENUM_TO_STR(SIGSEGV);
44 CASE_ENUM_TO_STR(SIGALRM);
45 CASE_ENUM_TO_STR(SIGUSR1);
46 CASE_ENUM_TO_STR(SIGUSR2);
47 CASE_ENUM_TO_STR(SIGCONT);
48 CASE_ENUM_TO_STR(SIGSTOP);
49 CASE_ENUM_TO_STR(SIGTSTP);
50 CASE_ENUM_TO_STR(SIGTTIN);
51 CASE_ENUM_TO_STR(SIGTTOU);
52 CASE_ENUM_TO_STR(SIGTRAP);
53 CASE_ENUM_TO_STR(SIGBUS);
54 CASE_ENUM_TO_STR(SIGSTKFLT);
55 CASE_ENUM_TO_STR(SIGCHLD);
56 CASE_ENUM_TO_STR(SIGIO);
57 CASE_ENUM_TO_STR(SIGPROF);
58 CASE_ENUM_TO_STR(SIGSYS);
59 CASE_ENUM_TO_STR(SIGWINCH);
60 CASE_ENUM_TO_STR(SIGPWR);
61 CASE_ENUM_TO_STR(SIGURG);
62 CASE_ENUM_TO_STR(SIGXCPU);
63 CASE_ENUM_TO_STR(SIGVTALRM);
64 CASE_ENUM_TO_STR(SIGXFSZ);
65 default: ss << "unknown (" << raw << ")"; break;
66 }
67 } else if (raw >= SIGRTMIN_PRIV && raw < SIGRTMIN) {
68 ss << "glibc internal signal";
69 } else {
70 ss << "SIGRT" << raw - SIGRTMIN;
71 }
72
73 if (verbose) {
74 ss << " (" << cosmos::Signal{signal}.name() << ")";
75 }
76
77 return ss.str();
78}
79
80std::string signal_set(const cosmos::SigSet &set) {
81 std::stringstream ss;
82
83 ss << "{";
84
85 for (int signum = 1; signum < SIGRTMAX; signum++) {
86 const auto sig = cosmos::SignalNr{signum};
87
88 if (set.isSet(cosmos::Signal{sig})) {
89 ss << format::signal(sig, /*verbose=*/false) << ", ";
90 }
91 }
92
93 auto ret = ss.str();
94 if (cosmos::is_suffix(ret, ", ")) {
95 ret = ret.substr(0, ret.size() - 2);
96 }
97
98 return ret + "}";
99}
100
101std::string saflags(const int flags) {
102#define chk_sa_flag(FLAG) if (flags & FLAG) { \
103 if (!first) ss << "|";\
104 else first = false;\
105 \
106 ss << #FLAG;\
107}
108
109 if (flags == 0) {
110 return "0";
111 }
112
113 std::stringstream ss;
114 bool first = true;
115
116 chk_sa_flag(SA_NOCLDSTOP);
117 chk_sa_flag(SA_NOCLDWAIT);
118 chk_sa_flag(SA_NODEFER);
119 chk_sa_flag(SA_ONSTACK);
120 chk_sa_flag(SA_RESETHAND);
121 chk_sa_flag(SA_RESTART);
122 chk_sa_flag(SA_SIGINFO);
123 chk_sa_flag(SA_RESTORER);
124
125 return ss.str();
126}
127
128std::string limit(const uint64_t lim) {
129 if (lim == RLIM_INFINITY)
130 return "RLIM_INFINITY";
131 // these seem to have the same value on some platforms, triggering
132 // -Wduplicated-cond. So only make the check if the constants actually
133 // differ.
134#if RLIM_INFINITY != RLIM64_INFINITY
135 else if (lim == RLIM64_INFINITY)
136 return "RLIM64_INFINITY";
137#endif
138 else if ((lim % 1024) == 0)
139 return std::to_string(lim / 1024) + " * " + "1024";
140 else
141 return std::to_string(lim);
142}
143
144std::string si_code(const cosmos::SigInfo::Source src) {
145 const auto raw = cosmos::to_integral(src);
146
147 switch (raw) {
148 CASE_ENUM_TO_STR(SI_USER);
149 CASE_ENUM_TO_STR(SI_KERNEL);
150 CASE_ENUM_TO_STR(SI_QUEUE);
151 CASE_ENUM_TO_STR(SI_TIMER);
152 CASE_ENUM_TO_STR(SI_MESGQ);
153 CASE_ENUM_TO_STR(SI_ASYNCIO);
154 CASE_ENUM_TO_STR(SI_SIGIO);
155 CASE_ENUM_TO_STR(SI_TKILL);
156 default: return std::to_string(raw);
157 }
158}
159
160std::string si_reason(const cosmos::SigInfo::SysData::Reason reason) {
161 const auto raw = cosmos::to_integral(reason);
162
163 switch (raw) {
164 CASE_ENUM_TO_STR(SYS_SECCOMP);
165 CASE_ENUM_TO_STR(SYS_USER_DISPATCH);
166 default: return std::to_string(raw);
167 }
168}
169
170std::string si_reason(const cosmos::SigInfo::SegfaultData::Reason reason) {
171 const auto raw = cosmos::to_integral(reason);
172
173 switch (raw) {
174 CASE_ENUM_TO_STR(SEGV_MAPERR);
175 CASE_ENUM_TO_STR(SEGV_ACCERR);
176 CASE_ENUM_TO_STR(SEGV_BNDERR);
177 CASE_ENUM_TO_STR(SEGV_PKUERR);
178 CASE_ENUM_TO_STR(SEGV_ACCADI);
179 CASE_ENUM_TO_STR(SEGV_ADIDERR);
180 CASE_ENUM_TO_STR(SEGV_ADIPERR);
181 CASE_ENUM_TO_STR(SEGV_MTEAERR);
182 CASE_ENUM_TO_STR(SEGV_MTESERR);
183#ifdef SEGV_CPERR
184 CASE_ENUM_TO_STR(SEGV_CPERR);
185#endif
186 default: return std::to_string(raw);
187 }
188}
189
190std::string si_reason(const cosmos::SigInfo::BusData::Reason reason) {
191 const auto raw = cosmos::to_integral(reason);
192
193 switch (raw) {
194 CASE_ENUM_TO_STR(BUS_ADRALN);
195 CASE_ENUM_TO_STR(BUS_ADRERR);
196 CASE_ENUM_TO_STR(BUS_OBJERR);
197 CASE_ENUM_TO_STR(BUS_MCEERR_AR);
198 CASE_ENUM_TO_STR(BUS_MCEERR_AO);
199 default: return std::to_string(raw);
200 }
201}
202
203std::string ptrace_arch(const cosmos::ptrace::Arch arch) {
204 const auto raw = cosmos::to_integral(arch);
205
206 switch (raw) {
207 CASE_ENUM_TO_STR(AUDIT_ARCH_X86_64);
208 CASE_ENUM_TO_STR(AUDIT_ARCH_I386);
209 default: return std::to_string(raw);
210 }
211}
212
213std::string child_event(const cosmos::SigInfo::ChildData::Event event) {
214 const auto raw = cosmos::to_integral(event);
215
216 switch (raw) {
217 CASE_ENUM_TO_STR(CLD_EXITED);
218 CASE_ENUM_TO_STR(CLD_KILLED);
219 CASE_ENUM_TO_STR(CLD_DUMPED);
220 CASE_ENUM_TO_STR(CLD_TRAPPED);
221 CASE_ENUM_TO_STR(CLD_STOPPED);
222 CASE_ENUM_TO_STR(CLD_CONTINUED);
223 default: return std::to_string(raw);
224 }
225}
226
227std::string si_reason(const cosmos::SigInfo::PollData::Reason reason) {
228 const auto raw = cosmos::to_integral(reason);
229
230 switch (raw) {
231 CASE_ENUM_TO_STR(POLL_IN);
232 CASE_ENUM_TO_STR(POLL_OUT);
233 CASE_ENUM_TO_STR(POLL_MSG);
234 CASE_ENUM_TO_STR(POLL_ERR);
235 CASE_ENUM_TO_STR(POLL_PRI);
236 CASE_ENUM_TO_STR(POLL_HUP);
237 default: return std::to_string(raw);
238 }
239}
240
241std::string si_reason(const cosmos::SigInfo::IllData::Reason reason) {
242 const auto raw = cosmos::to_integral(reason);
243
244 switch (raw) {
245 CASE_ENUM_TO_STR(ILL_ILLOPC);
246 CASE_ENUM_TO_STR(ILL_ILLOPN);
247 CASE_ENUM_TO_STR(ILL_ILLADR);
248 CASE_ENUM_TO_STR(ILL_ILLTRP);
249 CASE_ENUM_TO_STR(ILL_PRVOPC);
250 CASE_ENUM_TO_STR(ILL_PRVREG);
251 CASE_ENUM_TO_STR(ILL_COPROC);
252 CASE_ENUM_TO_STR(ILL_BADSTK);
253 CASE_ENUM_TO_STR(ILL_BADIADDR);
254 default: return std::to_string(raw);
255 }
256}
257
258std::string si_reason(const cosmos::SigInfo::FPEData::Reason reason) {
259 const auto raw = cosmos::to_integral(reason);
260
261 switch (raw) {
262 CASE_ENUM_TO_STR(FPE_INTDIV);
263 CASE_ENUM_TO_STR(FPE_INTOVF);
264 CASE_ENUM_TO_STR(FPE_FLTDIV);
265 CASE_ENUM_TO_STR(FPE_FLTOVF);
266 CASE_ENUM_TO_STR(FPE_FLTUND);
267 CASE_ENUM_TO_STR(FPE_FLTRES);
268 CASE_ENUM_TO_STR(FPE_FLTINV);
269 CASE_ENUM_TO_STR(FPE_FLTSUB);
270 CASE_ENUM_TO_STR(FPE_FLTUNK);
271 CASE_ENUM_TO_STR(FPE_CONDTRAP);
272 default: return std::to_string(raw);
273 }
274}
275
276std::string poll_event(const cosmos::PollEvent event) {
277 const auto raw = cosmos::to_integral(event);
278
279 switch (raw) {
280 CASE_ENUM_TO_STR(POLLIN);
281 CASE_ENUM_TO_STR(POLLPRI);
282 CASE_ENUM_TO_STR(POLLOUT);
283 CASE_ENUM_TO_STR(POLLRDHUP);
284 CASE_ENUM_TO_STR(POLLERR);
285 CASE_ENUM_TO_STR(POLLHUP);
286 CASE_ENUM_TO_STR(POLLNVAL);
287 CASE_ENUM_TO_STR(POLLWRBAND);
288 default: return std::to_string(raw);
289 }
290}
291
292std::string poll_events(const cosmos::PollEvents events) {
293 constexpr auto HIGHEST = 1 << (sizeof(cosmos::PollEvent) * 8 - 1);
294 std::string ret;
295
296 size_t bit = 1;
297 do {
298 const auto flag = cosmos::PollEvent{static_cast<cosmos::PollEvents::EnumBaseType>(bit)};
299
300 if (events[flag]) {
301 if (bit != 1) {
302 ret += "|";
303 }
304 ret += poll_event(flag);
305 }
306
307 bit <<= 1;
308 } while (bit != HIGHEST);
309
310 return ret;
311}
312
313std::string sig_info(const cosmos::SigInfo &info) {
314 using SigInfo = cosmos::SigInfo;
315 std::stringstream ss;
316
317 auto add_process_ctx = [&ss](const cosmos::ProcessCtx ctx) {
318 ss << ", si_pid=" << cosmos::to_integral(ctx.pid);
319 ss << ", si_uid=" << cosmos::to_integral(ctx.uid);
320 };
321
322 auto add_custom_data = [&ss](const SigInfo::CustomData data) {
323 ss << ", si_int=" << data.asInt();
324 ss << ", si_ptr=" << data.asPtr();
325 };
326
327 auto add_fault_data = [&ss](auto data) {
328 // translated si_code
329 ss << " (" << format::si_reason(data.reason) << ")";
330 ss << ", si_addr=" << data.addr;
331 };
332
333 ss << info.sigNr() << " {";
334 if (info.source() != SigInfo::Source::KERNEL ||
335 info.raw()->si_code == SI_KERNEL) {
336 ss << "si_code=" << format::si_code(info.source());
337 } else {
338 // we have to use the raw number instead
339 ss << "si_code=" << info.raw()->si_code;
340 }
341
342 if (auto user_data = info.userSigData(); user_data) {
343 add_process_ctx(user_data->sender);
344 } else if (auto queue_data = info.queueSigData(); queue_data) {
345 add_process_ctx(queue_data->sender);
346 add_custom_data(queue_data->data);
347 } else if (auto msg_queue_data = info.msgQueueData(); msg_queue_data) {
348 add_process_ctx(msg_queue_data->msg_sender);
349 add_custom_data(msg_queue_data->data);
350 } else if (auto timer_data = info.timerData(); timer_data) {
351 ss << ", si_timerid=" << cosmos::to_integral(timer_data->id);
352 ss << ", si_overrun=" << timer_data->overrun;
353 } else if (auto sys_data = info.sysData(); sys_data) {
354 ss << ", reason=" << format::si_reason(sys_data->reason);
355 ss << ", si_addr=" << sys_data->call_addr;
356 ss << ", si_syscall=" << sys_data->call_nr
357 << "(" << SystemCall::name(SystemCallNr{static_cast<uint64_t>(sys_data->call_nr)})
358 << ")";
359 ss << ", si_arch=" << format::ptrace_arch(sys_data->arch);
360 ss << ", si_errno=" << sys_data->error;
361 } else if (auto child_data = info.childData(); child_data) {
362 // translated si_code
363 ss << " (" << format::child_event(child_data->event) << ")";
364 add_process_ctx(child_data->child);
365 ss << ", si_status=" << info.raw()->si_status;
366 if (child_data->signal) {
367 ss << " (" << *child_data->signal << ")";
368 } else {
369 ss << " (exit code)";
370 }
371 if (child_data->user_time) {
372 ss << ", si_utime=" << cosmos::to_integral(*child_data->user_time);
373 }
374 if (child_data->system_time) {
375 ss << ", si_stime=" << cosmos::to_integral(*child_data->system_time);
376 }
377 } else if (auto poll_data = info.pollData(); poll_data) {
378 // translated si_code
379 ss << " (" << format::si_reason(poll_data->reason) << ")";
380 ss << ", si_fd=" << cosmos::to_integral(poll_data->fd);
381 ss << ", si_band=" << format::poll_events(poll_data->events);
382 } else if (auto ill_data = info.illData(); ill_data) {
383 add_fault_data(*ill_data);
384 } else if (auto fpe_data = info.fpeData(); fpe_data) {
385 add_fault_data(*fpe_data);
386 } else if (auto segv_data = info.segfaultData(); segv_data) {
387 add_fault_data(*segv_data);
388 if (auto bound = segv_data->bound; bound) {
389 ss << ", si_lower=" << bound->lower;
390 ss << ", si_upper=" << bound->upper;
391 }
392 if (auto key = segv_data->key; key) {
393 ss << ", si_pkey=" << cosmos::to_integral(*key);
394 }
395 } else if (auto bus_data = info.busData(); bus_data) {
396 add_fault_data(*bus_data);
397 if (auto lsb = bus_data->addr_lsb; lsb) {
398 ss << ", si_addr_lsb=" << *lsb;
399 }
400 }
401
402 ss << "}";
403
404 return ss.str();
405}
406
407std::string event(const cosmos::ChildState &state) {
408 std::stringstream ss;
409
410 ss << "PID " << cosmos::to_integral(state.child.pid) << " ";
411
412 if (state.exited()) {
413 ss << "EXITED with " << cosmos::to_integral(*state.status);
414 } else if (state.trapped()) {
415 ss << "TRAPPED";
416 if (state.signal->isPtraceEventStop()) {
417 const auto [_, event] = cosmos::ptrace::decode_event(*state.signal);
418 ss << " (PTRACE_EVENT_" << get_ptrace_event_str(event) << ")";
419 }
420 } else {
421 if (state.killed())
422 ss << "KILLED ";
423 else if (state.dumped())
424 ss << "DUMPED ";
425 else if (state.continued())
426 ss << "CONTINUED ";
427 else if (state.stopped())
428 ss << "STOPPED ";
429 ss << "(" << state.signal->name() << ")";
430 }
431
432 return ss.str();
433}
434
435std::string_view file_type(const cosmos::FileType type) {
436 switch (cosmos::to_integral(type.raw())) {
437 CASE_ENUM_TO_STR(S_IFSOCK);
438 CASE_ENUM_TO_STR(S_IFLNK);
439 CASE_ENUM_TO_STR(S_IFREG);
440 CASE_ENUM_TO_STR(S_IFBLK);
441 CASE_ENUM_TO_STR(S_IFDIR);
442 CASE_ENUM_TO_STR(S_IFCHR);
443 CASE_ENUM_TO_STR(S_IFIFO);
444 default: return "???";
445 }
446}
447
448std::string file_mode_numeric(const cosmos::FileModeBits mode) {
449 if (mode.none()) {
450 return "0";
451 }
452
453 return static_cast<std::string>(cosmos::OctNum{mode.raw(), 3});
454}
455
456std::string device_id(const cosmos::DeviceID id) {
457 const auto [major, minor] = cosmos::fs::split_device_id(id);
458 return cosmos::sprintf("%s:%s",
459 static_cast<std::string>(cosmos::HexNum{cosmos::to_integral(major), 2}).c_str(),
460 static_cast<std::string>(cosmos::HexNum{cosmos::to_integral(minor), 2}).c_str());
461
462}
463
464std::string timespec(const struct timespec &ts, const bool only_secs) {
465 std::stringstream ss;
466 if (only_secs) {
467 ss << ts.tv_sec << "s";
468 } else {
469 ss << "{" << ts.tv_sec << "s, " << ts.tv_nsec << "ns}";
470 }
471 return ss.str();
472}
473
474std::string timeval(const struct timeval &tv, const bool only_secs) {
475 std::stringstream ss;
476 if (only_secs) {
477 ss << tv.tv_sec << "s";
478 } else {
479 ss << "{" << tv.tv_sec << "s, " << tv.tv_usec << "us}";
480 }
481 return ss.str();
482}
483
484struct HexChar :
485 public cosmos::FormattedNumber<unsigned char> {
486 HexChar(const unsigned char ch) :
487 cosmos::FormattedNumber<unsigned char>{ch, 2, [](std::ostream &o){ o << std::hex; }, "\\x"}
488 {}
489};
490
491std::string control_char(const char ch) {
492 switch(ch) {
493 case '\n': return "\\n";
494 case '\r': return "\\r";
495 case '\v': return "\\v";
496 case '\t': return "\\t";
497 case '\f': return "\\f";
498 case '\b': return "\\b";
499 default: return "\\?";
500 }
501}
502
503std::string buffer(const uint8_t *buffer, const size_t len) {
504 std::stringstream ss;
505 ss << '"';
506
507 /*
508 * By default show printable characters and everything else as hex
509 * bytes. This can be made configurable via input parameters and/or a
510 * global setting for achieve behaviour like:
511 *
512 * - hex output only
513 * - oct output only
514 * - text output only, discard anything else
515 */
516
517 // TODO: what about unicode strings?
518
519 for (size_t idx = 0; idx < len; idx++) {
520 const auto ch = static_cast<unsigned char>(buffer[idx]);
521
522 if (std::isprint(ch)) {
523 ss << ch;
524 } else if (std::isspace(ch)) {
525 ss << control_char(ch);
526 } else {
527 ss << HexChar{ch};
528 }
529 }
530
531 ss << '"';
532
533 return ss.str();
534}
535
536std::string pointer(const ForeignPtr ptr) {
537 if (ptr == ForeignPtr::NO_POINTER)
538 return "NULL";
539
540 std::stringstream ss;
541 ss << reinterpret_cast<void*>(cosmos::to_integral(ptr));
542
543 return ss.str();
544}
545
546std::string pointer(const ForeignPtr ptr, const std::string_view data) {
547 if (ptr == ForeignPtr::NO_POINTER)
548 return "NULL";
549
550 std::stringstream ss;
551
552 ss << reinterpret_cast<void*>(cosmos::to_integral(ptr)) << " → " << "[" << data << "]";
553
554 return ss.str();
555}
556
557std::string_view fd_type(const FDInfo &info) {
558 using enum FDInfo::Type;
559
560 switch (info.type) {
561 default:
562 CASE_ENUM_TO_STR(INVALID);
563 CASE_ENUM_TO_STR(FS_PATH);
564 CASE_ENUM_TO_STR(EVENT_FD);
565 CASE_ENUM_TO_STR(TIMER_FD);
566 CASE_ENUM_TO_STR(SIGNAL_FD);
567 CASE_ENUM_TO_STR(SOCKET);
568 CASE_ENUM_TO_STR(EPOLL);
569 CASE_ENUM_TO_STR(PIPE);
570 CASE_ENUM_TO_STR(INOTIFY);
571 CASE_ENUM_TO_STR(PID_FD);
572 CASE_ENUM_TO_STR(BPF_MAP);
573 CASE_ENUM_TO_STR(BPF_PROG);
574 CASE_ENUM_TO_STR(PERF_EVENT);
575 CASE_ENUM_TO_STR(UNKNOWN);
576 }
577}
578
579std::string fd_info(const FDInfo &info) {
580 using enum FDInfo::Type;
581
582 std::string extra_info;
583
584 switch (info.type) {
585 case FS_PATH: return cosmos::sprintf("<%s>", info.path.c_str());
586 case PIPE: {
587 const bool read_end = info.mode == cosmos::OpenMode::READ_ONLY;
588 extra_info = ","s + (read_end ? "ro"s : "wronly"s);
589 [[ fallthrough ]];
590 }
591 default: return cosmos::sprintf("<%s%s>", &fd_type(info)[0], extra_info.c_str());
592 }
593}
594
595} // end ns
std::string_view name() const
Returns the system call's human readable name.
const char * get_ptrace_event_str(const cosmos::ptrace::Event event)
Returns a string label for the given event.
Definition utils.cxx:47
SystemCallNr
Abstract system call number usable across architectures and ABIs.
Definition generic.hxx:29
ForeignPtr
Strongly typed opaque pointer to tracee memory.
Definition types.hxx:140
Type
Different types of file descriptors.
Definition types.hxx:86