├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── intentrace-example.jpg
├── intentrace-example.png
├── itrace.png
└── src
├── auxiliary.rs
├── cli.rs
├── colors.rs
├── macro_interps.rs
├── main.rs
├── one_line_formatter.rs
├── peeker_poker.rs
├── return_resolvers.rs
├── syscall_annotations_map.rs
├── syscall_categories.rs
├── syscall_object.rs
├── syscall_skeleton_map.rs
├── types.rs
├── utilities.rs
└── writer.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "adler2"
7 | version = "2.0.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
10 |
11 | [[package]]
12 | name = "android-tzdata"
13 | version = "0.1.1"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
16 |
17 | [[package]]
18 | name = "android_system_properties"
19 | version = "0.1.5"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
22 | dependencies = [
23 | "libc",
24 | ]
25 |
26 | [[package]]
27 | name = "anstream"
28 | version = "0.6.18"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
31 | dependencies = [
32 | "anstyle",
33 | "anstyle-parse",
34 | "anstyle-query",
35 | "anstyle-wincon",
36 | "colorchoice",
37 | "is_terminal_polyfill",
38 | "utf8parse",
39 | ]
40 |
41 | [[package]]
42 | name = "anstyle"
43 | version = "1.0.10"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
46 |
47 | [[package]]
48 | name = "anstyle-parse"
49 | version = "0.2.6"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
52 | dependencies = [
53 | "utf8parse",
54 | ]
55 |
56 | [[package]]
57 | name = "anstyle-query"
58 | version = "1.1.2"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
61 | dependencies = [
62 | "windows-sys 0.59.0",
63 | ]
64 |
65 | [[package]]
66 | name = "anstyle-wincon"
67 | version = "3.0.7"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
70 | dependencies = [
71 | "anstyle",
72 | "once_cell",
73 | "windows-sys 0.59.0",
74 | ]
75 |
76 | [[package]]
77 | name = "anyhow"
78 | version = "1.0.98"
79 | source = "registry+https://github.com/rust-lang/crates.io-index"
80 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
81 |
82 | [[package]]
83 | name = "autocfg"
84 | version = "1.4.0"
85 | source = "registry+https://github.com/rust-lang/crates.io-index"
86 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
87 |
88 | [[package]]
89 | name = "bitflags"
90 | version = "1.3.2"
91 | source = "registry+https://github.com/rust-lang/crates.io-index"
92 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
93 |
94 | [[package]]
95 | name = "bitflags"
96 | version = "2.9.0"
97 | source = "registry+https://github.com/rust-lang/crates.io-index"
98 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
99 |
100 | [[package]]
101 | name = "bumpalo"
102 | version = "3.17.0"
103 | source = "registry+https://github.com/rust-lang/crates.io-index"
104 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
105 |
106 | [[package]]
107 | name = "bytecount"
108 | version = "0.6.8"
109 | source = "registry+https://github.com/rust-lang/crates.io-index"
110 | checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
111 |
112 | [[package]]
113 | name = "cc"
114 | version = "1.2.17"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
117 | dependencies = [
118 | "shlex",
119 | ]
120 |
121 | [[package]]
122 | name = "cfg-if"
123 | version = "1.0.0"
124 | source = "registry+https://github.com/rust-lang/crates.io-index"
125 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
126 |
127 | [[package]]
128 | name = "cfg_aliases"
129 | version = "0.2.1"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
132 |
133 | [[package]]
134 | name = "chrono"
135 | version = "0.4.40"
136 | source = "registry+https://github.com/rust-lang/crates.io-index"
137 | checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
138 | dependencies = [
139 | "android-tzdata",
140 | "iana-time-zone",
141 | "num-traits",
142 | "windows-link",
143 | ]
144 |
145 | [[package]]
146 | name = "clap"
147 | version = "4.5.32"
148 | source = "registry+https://github.com/rust-lang/crates.io-index"
149 | checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
150 | dependencies = [
151 | "clap_builder",
152 | "clap_derive",
153 | ]
154 |
155 | [[package]]
156 | name = "clap_builder"
157 | version = "4.5.32"
158 | source = "registry+https://github.com/rust-lang/crates.io-index"
159 | checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
160 | dependencies = [
161 | "anstream",
162 | "anstyle",
163 | "clap_lex",
164 | "strsim",
165 | ]
166 |
167 | [[package]]
168 | name = "clap_derive"
169 | version = "4.5.32"
170 | source = "registry+https://github.com/rust-lang/crates.io-index"
171 | checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
172 | dependencies = [
173 | "heck",
174 | "proc-macro2",
175 | "quote",
176 | "syn",
177 | ]
178 |
179 | [[package]]
180 | name = "clap_lex"
181 | version = "0.7.4"
182 | source = "registry+https://github.com/rust-lang/crates.io-index"
183 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
184 |
185 | [[package]]
186 | name = "colorchoice"
187 | version = "1.0.3"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
190 |
191 | [[package]]
192 | name = "colored"
193 | version = "3.0.0"
194 | source = "registry+https://github.com/rust-lang/crates.io-index"
195 | checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
196 | dependencies = [
197 | "windows-sys 0.59.0",
198 | ]
199 |
200 | [[package]]
201 | name = "core-foundation-sys"
202 | version = "0.8.7"
203 | source = "registry+https://github.com/rust-lang/crates.io-index"
204 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
205 |
206 | [[package]]
207 | name = "crc32fast"
208 | version = "1.4.2"
209 | source = "registry+https://github.com/rust-lang/crates.io-index"
210 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
211 | dependencies = [
212 | "cfg-if",
213 | ]
214 |
215 | [[package]]
216 | name = "crossterm"
217 | version = "0.28.1"
218 | source = "registry+https://github.com/rust-lang/crates.io-index"
219 | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
220 | dependencies = [
221 | "bitflags 2.9.0",
222 | "crossterm_winapi",
223 | "mio",
224 | "parking_lot",
225 | "rustix",
226 | "signal-hook",
227 | "signal-hook-mio",
228 | "winapi",
229 | ]
230 |
231 | [[package]]
232 | name = "crossterm_winapi"
233 | version = "0.9.1"
234 | source = "registry+https://github.com/rust-lang/crates.io-index"
235 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
236 | dependencies = [
237 | "winapi",
238 | ]
239 |
240 | [[package]]
241 | name = "ctrlc"
242 | version = "3.4.5"
243 | source = "registry+https://github.com/rust-lang/crates.io-index"
244 | checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3"
245 | dependencies = [
246 | "nix 0.29.0",
247 | "windows-sys 0.59.0",
248 | ]
249 |
250 | [[package]]
251 | name = "deranged"
252 | version = "0.4.1"
253 | source = "registry+https://github.com/rust-lang/crates.io-index"
254 | checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
255 | dependencies = [
256 | "powerfmt",
257 | ]
258 |
259 | [[package]]
260 | name = "downcast"
261 | version = "0.11.0"
262 | source = "registry+https://github.com/rust-lang/crates.io-index"
263 | checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
264 |
265 | [[package]]
266 | name = "errno"
267 | version = "0.3.10"
268 | source = "registry+https://github.com/rust-lang/crates.io-index"
269 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
270 | dependencies = [
271 | "libc",
272 | "windows-sys 0.59.0",
273 | ]
274 |
275 | [[package]]
276 | name = "flate2"
277 | version = "1.1.0"
278 | source = "registry+https://github.com/rust-lang/crates.io-index"
279 | checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
280 | dependencies = [
281 | "crc32fast",
282 | "miniz_oxide",
283 | ]
284 |
285 | [[package]]
286 | name = "fnv"
287 | version = "1.0.7"
288 | source = "registry+https://github.com/rust-lang/crates.io-index"
289 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
290 |
291 | [[package]]
292 | name = "fragile"
293 | version = "2.0.1"
294 | source = "registry+https://github.com/rust-lang/crates.io-index"
295 | checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619"
296 |
297 | [[package]]
298 | name = "heck"
299 | version = "0.5.0"
300 | source = "registry+https://github.com/rust-lang/crates.io-index"
301 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
302 |
303 | [[package]]
304 | name = "hex"
305 | version = "0.4.3"
306 | source = "registry+https://github.com/rust-lang/crates.io-index"
307 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
308 |
309 | [[package]]
310 | name = "iana-time-zone"
311 | version = "0.1.62"
312 | source = "registry+https://github.com/rust-lang/crates.io-index"
313 | checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
314 | dependencies = [
315 | "android_system_properties",
316 | "core-foundation-sys",
317 | "iana-time-zone-haiku",
318 | "js-sys",
319 | "log",
320 | "wasm-bindgen",
321 | "windows-core",
322 | ]
323 |
324 | [[package]]
325 | name = "iana-time-zone-haiku"
326 | version = "0.1.2"
327 | source = "registry+https://github.com/rust-lang/crates.io-index"
328 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
329 | dependencies = [
330 | "cc",
331 | ]
332 |
333 | [[package]]
334 | name = "if_chain"
335 | version = "1.0.2"
336 | source = "registry+https://github.com/rust-lang/crates.io-index"
337 | checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
338 |
339 | [[package]]
340 | name = "intentrace"
341 | version = "0.10.3"
342 | dependencies = [
343 | "anyhow",
344 | "clap",
345 | "colored",
346 | "ctrlc",
347 | "if_chain",
348 | "nix 0.29.0",
349 | "num-traits",
350 | "pete",
351 | "procfs",
352 | "syscalls",
353 | "tabled",
354 | "termbg",
355 | "thousands",
356 | "unicode-segmentation",
357 | "uzers",
358 | ]
359 |
360 | [[package]]
361 | name = "is_terminal_polyfill"
362 | version = "1.70.1"
363 | source = "registry+https://github.com/rust-lang/crates.io-index"
364 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
365 |
366 | [[package]]
367 | name = "itoa"
368 | version = "1.0.15"
369 | source = "registry+https://github.com/rust-lang/crates.io-index"
370 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
371 |
372 | [[package]]
373 | name = "js-sys"
374 | version = "0.3.77"
375 | source = "registry+https://github.com/rust-lang/crates.io-index"
376 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
377 | dependencies = [
378 | "once_cell",
379 | "wasm-bindgen",
380 | ]
381 |
382 | [[package]]
383 | name = "libc"
384 | version = "0.2.171"
385 | source = "registry+https://github.com/rust-lang/crates.io-index"
386 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
387 |
388 | [[package]]
389 | name = "linux-raw-sys"
390 | version = "0.4.15"
391 | source = "registry+https://github.com/rust-lang/crates.io-index"
392 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
393 |
394 | [[package]]
395 | name = "lock_api"
396 | version = "0.4.12"
397 | source = "registry+https://github.com/rust-lang/crates.io-index"
398 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
399 | dependencies = [
400 | "autocfg",
401 | "scopeguard",
402 | ]
403 |
404 | [[package]]
405 | name = "log"
406 | version = "0.4.27"
407 | source = "registry+https://github.com/rust-lang/crates.io-index"
408 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
409 |
410 | [[package]]
411 | name = "memoffset"
412 | version = "0.7.1"
413 | source = "registry+https://github.com/rust-lang/crates.io-index"
414 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
415 | dependencies = [
416 | "autocfg",
417 | ]
418 |
419 | [[package]]
420 | name = "memoffset"
421 | version = "0.8.0"
422 | source = "registry+https://github.com/rust-lang/crates.io-index"
423 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
424 | dependencies = [
425 | "autocfg",
426 | ]
427 |
428 | [[package]]
429 | name = "miniz_oxide"
430 | version = "0.8.5"
431 | source = "registry+https://github.com/rust-lang/crates.io-index"
432 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
433 | dependencies = [
434 | "adler2",
435 | ]
436 |
437 | [[package]]
438 | name = "mio"
439 | version = "1.0.3"
440 | source = "registry+https://github.com/rust-lang/crates.io-index"
441 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
442 | dependencies = [
443 | "libc",
444 | "log",
445 | "wasi",
446 | "windows-sys 0.52.0",
447 | ]
448 |
449 | [[package]]
450 | name = "mockall"
451 | version = "0.13.1"
452 | source = "registry+https://github.com/rust-lang/crates.io-index"
453 | checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2"
454 | dependencies = [
455 | "cfg-if",
456 | "downcast",
457 | "fragile",
458 | "mockall_derive",
459 | "predicates",
460 | "predicates-tree",
461 | ]
462 |
463 | [[package]]
464 | name = "mockall_derive"
465 | version = "0.13.1"
466 | source = "registry+https://github.com/rust-lang/crates.io-index"
467 | checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898"
468 | dependencies = [
469 | "cfg-if",
470 | "proc-macro2",
471 | "quote",
472 | "syn",
473 | ]
474 |
475 | [[package]]
476 | name = "nix"
477 | version = "0.26.4"
478 | source = "registry+https://github.com/rust-lang/crates.io-index"
479 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
480 | dependencies = [
481 | "bitflags 1.3.2",
482 | "cfg-if",
483 | "libc",
484 | "memoffset 0.7.1",
485 | "pin-utils",
486 | ]
487 |
488 | [[package]]
489 | name = "nix"
490 | version = "0.29.0"
491 | source = "registry+https://github.com/rust-lang/crates.io-index"
492 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
493 | dependencies = [
494 | "bitflags 2.9.0",
495 | "cfg-if",
496 | "cfg_aliases",
497 | "libc",
498 | ]
499 |
500 | [[package]]
501 | name = "num-conv"
502 | version = "0.1.0"
503 | source = "registry+https://github.com/rust-lang/crates.io-index"
504 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
505 |
506 | [[package]]
507 | name = "num-traits"
508 | version = "0.2.19"
509 | source = "registry+https://github.com/rust-lang/crates.io-index"
510 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
511 | dependencies = [
512 | "autocfg",
513 | ]
514 |
515 | [[package]]
516 | name = "num_threads"
517 | version = "0.1.7"
518 | source = "registry+https://github.com/rust-lang/crates.io-index"
519 | checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
520 | dependencies = [
521 | "libc",
522 | ]
523 |
524 | [[package]]
525 | name = "once_cell"
526 | version = "1.21.1"
527 | source = "registry+https://github.com/rust-lang/crates.io-index"
528 | checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
529 |
530 | [[package]]
531 | name = "papergrid"
532 | version = "0.14.0"
533 | source = "registry+https://github.com/rust-lang/crates.io-index"
534 | checksum = "b915f831b85d984193fdc3d3611505871dc139b2534530fa01c1a6a6707b6723"
535 | dependencies = [
536 | "bytecount",
537 | "fnv",
538 | "unicode-width",
539 | ]
540 |
541 | [[package]]
542 | name = "parking_lot"
543 | version = "0.12.3"
544 | source = "registry+https://github.com/rust-lang/crates.io-index"
545 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
546 | dependencies = [
547 | "lock_api",
548 | "parking_lot_core",
549 | ]
550 |
551 | [[package]]
552 | name = "parking_lot_core"
553 | version = "0.9.10"
554 | source = "registry+https://github.com/rust-lang/crates.io-index"
555 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
556 | dependencies = [
557 | "cfg-if",
558 | "libc",
559 | "redox_syscall",
560 | "smallvec",
561 | "windows-targets",
562 | ]
563 |
564 | [[package]]
565 | name = "pete"
566 | version = "0.12.0"
567 | source = "registry+https://github.com/rust-lang/crates.io-index"
568 | checksum = "0f09c1c1ad40df294ff8643fe88a3dc64fff3293b6bc0ed9f71aff71f7086cbd"
569 | dependencies = [
570 | "libc",
571 | "memoffset 0.8.0",
572 | "nix 0.26.4",
573 | "thiserror 1.0.69",
574 | ]
575 |
576 | [[package]]
577 | name = "pin-utils"
578 | version = "0.1.0"
579 | source = "registry+https://github.com/rust-lang/crates.io-index"
580 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
581 |
582 | [[package]]
583 | name = "powerfmt"
584 | version = "0.2.0"
585 | source = "registry+https://github.com/rust-lang/crates.io-index"
586 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
587 |
588 | [[package]]
589 | name = "predicates"
590 | version = "3.1.3"
591 | source = "registry+https://github.com/rust-lang/crates.io-index"
592 | checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
593 | dependencies = [
594 | "anstyle",
595 | "predicates-core",
596 | ]
597 |
598 | [[package]]
599 | name = "predicates-core"
600 | version = "1.0.9"
601 | source = "registry+https://github.com/rust-lang/crates.io-index"
602 | checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
603 |
604 | [[package]]
605 | name = "predicates-tree"
606 | version = "1.0.12"
607 | source = "registry+https://github.com/rust-lang/crates.io-index"
608 | checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
609 | dependencies = [
610 | "predicates-core",
611 | "termtree",
612 | ]
613 |
614 | [[package]]
615 | name = "proc-macro-error-attr2"
616 | version = "2.0.0"
617 | source = "registry+https://github.com/rust-lang/crates.io-index"
618 | checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
619 | dependencies = [
620 | "proc-macro2",
621 | "quote",
622 | ]
623 |
624 | [[package]]
625 | name = "proc-macro-error2"
626 | version = "2.0.1"
627 | source = "registry+https://github.com/rust-lang/crates.io-index"
628 | checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
629 | dependencies = [
630 | "proc-macro-error-attr2",
631 | "proc-macro2",
632 | "quote",
633 | "syn",
634 | ]
635 |
636 | [[package]]
637 | name = "proc-macro2"
638 | version = "1.0.94"
639 | source = "registry+https://github.com/rust-lang/crates.io-index"
640 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
641 | dependencies = [
642 | "unicode-ident",
643 | ]
644 |
645 | [[package]]
646 | name = "procfs"
647 | version = "0.17.0"
648 | source = "registry+https://github.com/rust-lang/crates.io-index"
649 | checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
650 | dependencies = [
651 | "bitflags 2.9.0",
652 | "chrono",
653 | "flate2",
654 | "hex",
655 | "procfs-core",
656 | "rustix",
657 | ]
658 |
659 | [[package]]
660 | name = "procfs-core"
661 | version = "0.17.0"
662 | source = "registry+https://github.com/rust-lang/crates.io-index"
663 | checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
664 | dependencies = [
665 | "bitflags 2.9.0",
666 | "chrono",
667 | "hex",
668 | ]
669 |
670 | [[package]]
671 | name = "quote"
672 | version = "1.0.40"
673 | source = "registry+https://github.com/rust-lang/crates.io-index"
674 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
675 | dependencies = [
676 | "proc-macro2",
677 | ]
678 |
679 | [[package]]
680 | name = "redox_syscall"
681 | version = "0.5.10"
682 | source = "registry+https://github.com/rust-lang/crates.io-index"
683 | checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
684 | dependencies = [
685 | "bitflags 2.9.0",
686 | ]
687 |
688 | [[package]]
689 | name = "rustix"
690 | version = "0.38.44"
691 | source = "registry+https://github.com/rust-lang/crates.io-index"
692 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
693 | dependencies = [
694 | "bitflags 2.9.0",
695 | "errno",
696 | "libc",
697 | "linux-raw-sys",
698 | "windows-sys 0.59.0",
699 | ]
700 |
701 | [[package]]
702 | name = "rustversion"
703 | version = "1.0.20"
704 | source = "registry+https://github.com/rust-lang/crates.io-index"
705 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
706 |
707 | [[package]]
708 | name = "scopeguard"
709 | version = "1.2.0"
710 | source = "registry+https://github.com/rust-lang/crates.io-index"
711 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
712 |
713 | [[package]]
714 | name = "serde"
715 | version = "1.0.219"
716 | source = "registry+https://github.com/rust-lang/crates.io-index"
717 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
718 | dependencies = [
719 | "serde_derive",
720 | ]
721 |
722 | [[package]]
723 | name = "serde_derive"
724 | version = "1.0.219"
725 | source = "registry+https://github.com/rust-lang/crates.io-index"
726 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
727 | dependencies = [
728 | "proc-macro2",
729 | "quote",
730 | "syn",
731 | ]
732 |
733 | [[package]]
734 | name = "serde_repr"
735 | version = "0.1.20"
736 | source = "registry+https://github.com/rust-lang/crates.io-index"
737 | checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
738 | dependencies = [
739 | "proc-macro2",
740 | "quote",
741 | "syn",
742 | ]
743 |
744 | [[package]]
745 | name = "shlex"
746 | version = "1.3.0"
747 | source = "registry+https://github.com/rust-lang/crates.io-index"
748 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
749 |
750 | [[package]]
751 | name = "signal-hook"
752 | version = "0.3.17"
753 | source = "registry+https://github.com/rust-lang/crates.io-index"
754 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
755 | dependencies = [
756 | "libc",
757 | "signal-hook-registry",
758 | ]
759 |
760 | [[package]]
761 | name = "signal-hook-mio"
762 | version = "0.2.4"
763 | source = "registry+https://github.com/rust-lang/crates.io-index"
764 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
765 | dependencies = [
766 | "libc",
767 | "mio",
768 | "signal-hook",
769 | ]
770 |
771 | [[package]]
772 | name = "signal-hook-registry"
773 | version = "1.4.2"
774 | source = "registry+https://github.com/rust-lang/crates.io-index"
775 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
776 | dependencies = [
777 | "libc",
778 | ]
779 |
780 | [[package]]
781 | name = "simplelog"
782 | version = "0.12.2"
783 | source = "registry+https://github.com/rust-lang/crates.io-index"
784 | checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
785 | dependencies = [
786 | "log",
787 | "termcolor",
788 | "time",
789 | ]
790 |
791 | [[package]]
792 | name = "smallvec"
793 | version = "1.14.0"
794 | source = "registry+https://github.com/rust-lang/crates.io-index"
795 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
796 |
797 | [[package]]
798 | name = "strsim"
799 | version = "0.11.1"
800 | source = "registry+https://github.com/rust-lang/crates.io-index"
801 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
802 |
803 | [[package]]
804 | name = "syn"
805 | version = "2.0.100"
806 | source = "registry+https://github.com/rust-lang/crates.io-index"
807 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
808 | dependencies = [
809 | "proc-macro2",
810 | "quote",
811 | "unicode-ident",
812 | ]
813 |
814 | [[package]]
815 | name = "syscalls"
816 | version = "0.6.18"
817 | source = "registry+https://github.com/rust-lang/crates.io-index"
818 | checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe"
819 | dependencies = [
820 | "serde",
821 | "serde_repr",
822 | ]
823 |
824 | [[package]]
825 | name = "tabled"
826 | version = "0.18.0"
827 | source = "registry+https://github.com/rust-lang/crates.io-index"
828 | checksum = "121d8171ee5687a4978d1b244f7d99c43e7385a272185a2f1e1fa4dc0979d444"
829 | dependencies = [
830 | "papergrid",
831 | "tabled_derive",
832 | ]
833 |
834 | [[package]]
835 | name = "tabled_derive"
836 | version = "0.10.0"
837 | source = "registry+https://github.com/rust-lang/crates.io-index"
838 | checksum = "52d9946811baad81710ec921809e2af67ad77719418673b2a3794932d57b7538"
839 | dependencies = [
840 | "heck",
841 | "proc-macro-error2",
842 | "proc-macro2",
843 | "quote",
844 | "syn",
845 | ]
846 |
847 | [[package]]
848 | name = "termbg"
849 | version = "0.6.2"
850 | source = "registry+https://github.com/rust-lang/crates.io-index"
851 | checksum = "8bf44577a1adf3dfd7fec3b8763074467e27b2ad35ff9157bc3f0a51bb0a3dd4"
852 | dependencies = [
853 | "crossterm",
854 | "log",
855 | "mockall",
856 | "scopeguard",
857 | "simplelog",
858 | "thiserror 2.0.12",
859 | "winapi",
860 | ]
861 |
862 | [[package]]
863 | name = "termcolor"
864 | version = "1.4.1"
865 | source = "registry+https://github.com/rust-lang/crates.io-index"
866 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
867 | dependencies = [
868 | "winapi-util",
869 | ]
870 |
871 | [[package]]
872 | name = "termtree"
873 | version = "0.5.1"
874 | source = "registry+https://github.com/rust-lang/crates.io-index"
875 | checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
876 |
877 | [[package]]
878 | name = "thiserror"
879 | version = "1.0.69"
880 | source = "registry+https://github.com/rust-lang/crates.io-index"
881 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
882 | dependencies = [
883 | "thiserror-impl 1.0.69",
884 | ]
885 |
886 | [[package]]
887 | name = "thiserror"
888 | version = "2.0.12"
889 | source = "registry+https://github.com/rust-lang/crates.io-index"
890 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
891 | dependencies = [
892 | "thiserror-impl 2.0.12",
893 | ]
894 |
895 | [[package]]
896 | name = "thiserror-impl"
897 | version = "1.0.69"
898 | source = "registry+https://github.com/rust-lang/crates.io-index"
899 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
900 | dependencies = [
901 | "proc-macro2",
902 | "quote",
903 | "syn",
904 | ]
905 |
906 | [[package]]
907 | name = "thiserror-impl"
908 | version = "2.0.12"
909 | source = "registry+https://github.com/rust-lang/crates.io-index"
910 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
911 | dependencies = [
912 | "proc-macro2",
913 | "quote",
914 | "syn",
915 | ]
916 |
917 | [[package]]
918 | name = "thousands"
919 | version = "0.2.0"
920 | source = "registry+https://github.com/rust-lang/crates.io-index"
921 | checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
922 |
923 | [[package]]
924 | name = "time"
925 | version = "0.3.41"
926 | source = "registry+https://github.com/rust-lang/crates.io-index"
927 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
928 | dependencies = [
929 | "deranged",
930 | "itoa",
931 | "libc",
932 | "num-conv",
933 | "num_threads",
934 | "powerfmt",
935 | "serde",
936 | "time-core",
937 | "time-macros",
938 | ]
939 |
940 | [[package]]
941 | name = "time-core"
942 | version = "0.1.4"
943 | source = "registry+https://github.com/rust-lang/crates.io-index"
944 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
945 |
946 | [[package]]
947 | name = "time-macros"
948 | version = "0.2.22"
949 | source = "registry+https://github.com/rust-lang/crates.io-index"
950 | checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
951 | dependencies = [
952 | "num-conv",
953 | "time-core",
954 | ]
955 |
956 | [[package]]
957 | name = "unicode-ident"
958 | version = "1.0.18"
959 | source = "registry+https://github.com/rust-lang/crates.io-index"
960 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
961 |
962 | [[package]]
963 | name = "unicode-segmentation"
964 | version = "1.12.0"
965 | source = "registry+https://github.com/rust-lang/crates.io-index"
966 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
967 |
968 | [[package]]
969 | name = "unicode-width"
970 | version = "0.2.0"
971 | source = "registry+https://github.com/rust-lang/crates.io-index"
972 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
973 |
974 | [[package]]
975 | name = "utf8parse"
976 | version = "0.2.2"
977 | source = "registry+https://github.com/rust-lang/crates.io-index"
978 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
979 |
980 | [[package]]
981 | name = "uzers"
982 | version = "0.12.1"
983 | source = "registry+https://github.com/rust-lang/crates.io-index"
984 | checksum = "4df81ff504e7d82ad53e95ed1ad5b72103c11253f39238bcc0235b90768a97dd"
985 | dependencies = [
986 | "libc",
987 | "log",
988 | ]
989 |
990 | [[package]]
991 | name = "wasi"
992 | version = "0.11.0+wasi-snapshot-preview1"
993 | source = "registry+https://github.com/rust-lang/crates.io-index"
994 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
995 |
996 | [[package]]
997 | name = "wasm-bindgen"
998 | version = "0.2.100"
999 | source = "registry+https://github.com/rust-lang/crates.io-index"
1000 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
1001 | dependencies = [
1002 | "cfg-if",
1003 | "once_cell",
1004 | "rustversion",
1005 | "wasm-bindgen-macro",
1006 | ]
1007 |
1008 | [[package]]
1009 | name = "wasm-bindgen-backend"
1010 | version = "0.2.100"
1011 | source = "registry+https://github.com/rust-lang/crates.io-index"
1012 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
1013 | dependencies = [
1014 | "bumpalo",
1015 | "log",
1016 | "proc-macro2",
1017 | "quote",
1018 | "syn",
1019 | "wasm-bindgen-shared",
1020 | ]
1021 |
1022 | [[package]]
1023 | name = "wasm-bindgen-macro"
1024 | version = "0.2.100"
1025 | source = "registry+https://github.com/rust-lang/crates.io-index"
1026 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
1027 | dependencies = [
1028 | "quote",
1029 | "wasm-bindgen-macro-support",
1030 | ]
1031 |
1032 | [[package]]
1033 | name = "wasm-bindgen-macro-support"
1034 | version = "0.2.100"
1035 | source = "registry+https://github.com/rust-lang/crates.io-index"
1036 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
1037 | dependencies = [
1038 | "proc-macro2",
1039 | "quote",
1040 | "syn",
1041 | "wasm-bindgen-backend",
1042 | "wasm-bindgen-shared",
1043 | ]
1044 |
1045 | [[package]]
1046 | name = "wasm-bindgen-shared"
1047 | version = "0.2.100"
1048 | source = "registry+https://github.com/rust-lang/crates.io-index"
1049 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
1050 | dependencies = [
1051 | "unicode-ident",
1052 | ]
1053 |
1054 | [[package]]
1055 | name = "winapi"
1056 | version = "0.3.9"
1057 | source = "registry+https://github.com/rust-lang/crates.io-index"
1058 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1059 | dependencies = [
1060 | "winapi-i686-pc-windows-gnu",
1061 | "winapi-x86_64-pc-windows-gnu",
1062 | ]
1063 |
1064 | [[package]]
1065 | name = "winapi-i686-pc-windows-gnu"
1066 | version = "0.4.0"
1067 | source = "registry+https://github.com/rust-lang/crates.io-index"
1068 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1069 |
1070 | [[package]]
1071 | name = "winapi-util"
1072 | version = "0.1.9"
1073 | source = "registry+https://github.com/rust-lang/crates.io-index"
1074 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
1075 | dependencies = [
1076 | "windows-sys 0.59.0",
1077 | ]
1078 |
1079 | [[package]]
1080 | name = "winapi-x86_64-pc-windows-gnu"
1081 | version = "0.4.0"
1082 | source = "registry+https://github.com/rust-lang/crates.io-index"
1083 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1084 |
1085 | [[package]]
1086 | name = "windows-core"
1087 | version = "0.52.0"
1088 | source = "registry+https://github.com/rust-lang/crates.io-index"
1089 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
1090 | dependencies = [
1091 | "windows-targets",
1092 | ]
1093 |
1094 | [[package]]
1095 | name = "windows-link"
1096 | version = "0.1.1"
1097 | source = "registry+https://github.com/rust-lang/crates.io-index"
1098 | checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
1099 |
1100 | [[package]]
1101 | name = "windows-sys"
1102 | version = "0.52.0"
1103 | source = "registry+https://github.com/rust-lang/crates.io-index"
1104 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
1105 | dependencies = [
1106 | "windows-targets",
1107 | ]
1108 |
1109 | [[package]]
1110 | name = "windows-sys"
1111 | version = "0.59.0"
1112 | source = "registry+https://github.com/rust-lang/crates.io-index"
1113 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
1114 | dependencies = [
1115 | "windows-targets",
1116 | ]
1117 |
1118 | [[package]]
1119 | name = "windows-targets"
1120 | version = "0.52.6"
1121 | source = "registry+https://github.com/rust-lang/crates.io-index"
1122 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
1123 | dependencies = [
1124 | "windows_aarch64_gnullvm",
1125 | "windows_aarch64_msvc",
1126 | "windows_i686_gnu",
1127 | "windows_i686_gnullvm",
1128 | "windows_i686_msvc",
1129 | "windows_x86_64_gnu",
1130 | "windows_x86_64_gnullvm",
1131 | "windows_x86_64_msvc",
1132 | ]
1133 |
1134 | [[package]]
1135 | name = "windows_aarch64_gnullvm"
1136 | version = "0.52.6"
1137 | source = "registry+https://github.com/rust-lang/crates.io-index"
1138 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1139 |
1140 | [[package]]
1141 | name = "windows_aarch64_msvc"
1142 | version = "0.52.6"
1143 | source = "registry+https://github.com/rust-lang/crates.io-index"
1144 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1145 |
1146 | [[package]]
1147 | name = "windows_i686_gnu"
1148 | version = "0.52.6"
1149 | source = "registry+https://github.com/rust-lang/crates.io-index"
1150 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
1151 |
1152 | [[package]]
1153 | name = "windows_i686_gnullvm"
1154 | version = "0.52.6"
1155 | source = "registry+https://github.com/rust-lang/crates.io-index"
1156 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
1157 |
1158 | [[package]]
1159 | name = "windows_i686_msvc"
1160 | version = "0.52.6"
1161 | source = "registry+https://github.com/rust-lang/crates.io-index"
1162 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1163 |
1164 | [[package]]
1165 | name = "windows_x86_64_gnu"
1166 | version = "0.52.6"
1167 | source = "registry+https://github.com/rust-lang/crates.io-index"
1168 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1169 |
1170 | [[package]]
1171 | name = "windows_x86_64_gnullvm"
1172 | version = "0.52.6"
1173 | source = "registry+https://github.com/rust-lang/crates.io-index"
1174 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1175 |
1176 | [[package]]
1177 | name = "windows_x86_64_msvc"
1178 | version = "0.52.6"
1179 | source = "registry+https://github.com/rust-lang/crates.io-index"
1180 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1181 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "intentrace"
3 | version = "0.10.3"
4 | description = "intentrace is strace with intent, it goes all the way for you instead of half the way."
5 | edition = "2021"
6 | license = "MIT"
7 | repository = "https://github.com/sectordistrict/intentrace"
8 |
9 | [dependencies]
10 | anyhow = "1.0.98"
11 | clap = { version = "4.5.32", features = ["derive"] }
12 | colored = "3.0.0"
13 | ctrlc = { version = "3.4.5", features = ["termination"] }
14 | if_chain = "1.0.2"
15 | nix = { version = "0.29.0", features = ["ptrace", "uio", "signal"] }
16 | num-traits = "0.2.19"
17 | pete = { version = "0.12.0" }
18 | procfs = "0.17.0"
19 | syscalls = "0.6.18"
20 | tabled = "0.18.0"
21 | termbg = "0.6.2"
22 | thousands = "0.2.0"
23 | unicode-segmentation = "1.12.0"
24 | uzers = "0.12.1"
25 |
26 | # [profile.release]
27 | # lto = true
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # About
6 |
7 | intentrace is a strace for everyone, intentrace works similarly to strace in that it intercepts and records system calls when a process issues them, it then reasons through these syscalls by consulting an enormous backlog of syscall deduction heuristics.
8 | Due to the fact that linux syscalls almost always have dual usage that's obfuscated by libraries, seeing what a syscall is exactly asking for is immensely useful when e.g. a programmer is debugging a crashing binary.
9 |
10 |
11 |
12 |
13 |
14 | Intentrace follows a similar working methodology to the one employed by the [UniKraft kernel](https://github.com/unikraft) in that it attempts to cover a high percentage of the most popular linux software despite supporting only around 166 syscalls out of the 380+ linux syscalls (see page 8 of the Unikraft Paper for an example of strategic syscall coverage: https://arxiv.org/pdf/2104.12721). It's planned eventually for intentrace to cover all linux syscalls.
15 |
16 | ## Usage
17 |
18 | #### to quickly see how intentrace works in action, you can run simple examples
19 |
20 | `intentrace ls`
21 |
22 | `intentrace google-chrome`
23 |
24 | #### to disable program output from cluttering the syscall feed add `-q`
25 |
26 | `intentrace -q ls`
27 |
28 | #### to include the child processes of multi-threaded programs add `-f`
29 |
30 | `intentrace -f docker run alpine`
31 |
32 | | Parameter | Description | Default value |
33 | | ----------------------------- | --------------------------------------------------------------------------- | ------------- |
34 | | -c --summary-only | print a summary table | `false` |
35 | | -C --summary | print a summary table in addition to the normal output | `false` |
36 | | -p `pid` --attach `pid` | attach to an already running proceess | `not used` |
37 | | -o `file` --output `file` | redirect intentrace's output to a provided file | `not used` |
38 | | --trace=`syscall1,syscall2` | trace a specific syscall or a group of syscalls delimited by ',' | `not used` |
39 | | -f --follow-forks | trace child process when traced programs create them | `false` |
40 | | -Z --failed-only | only print failed syscalls | `false` |
41 | | -q --mute-stdout | mute traced program's std output | `false` |
42 |
43 | ## Installation
44 |
45 | ### Build from source
46 |
47 | Prerequisites:
48 |
49 | - Latest stable version of [Rust](https://www.rust-lang.org/tools/install) and Cargo.
50 |
51 | Build and run intentrace:
52 |
53 | ```
54 | git clone https://github.com/sectordistrict/intentrace.git
55 | cd intentrace
56 | cargo build --release
57 | ```
58 |
59 | ### Install from crates.io:
60 |
61 | ```
62 | cargo install intentrace
63 | ```
64 |
65 | ### Package Manager Availability
66 |
67 | [](https://repology.org/project/intentrace/versions)
68 |
69 | ## Project status
70 |
71 | intentrace is currently in beta, currently multi-threaded programs are a hit and miss.
72 |
73 | intentrace was originally intended to be a 2 window TUI, where a top panel shows a normal stream of syscalls, and a bottom panel contains metadata and clarifications, but this was abandoned in favor of the current scheme.
74 |
75 | #### Supported architecture
76 |
77 | intentrace currently only supports `x86-64`, given that the program is currently in beta, PRs for cross compatibility will unfortunately not be accepted until the program is stable enough.
78 |
79 | ## Contributing
80 |
81 | Support intentrace by contributing issues and PRs, don't feel discouraged from filing issues or creating PRs. Reading the source is a great way to learn how linux works.
82 |
83 | Feel free to file Issues and open Pull Requests that tackle any of:
84 |
85 | - providing more optimal phrasing/wording.
86 | - suggestions for granularity.
87 | - suggestions for fixes.
88 | - etc.. there are no rules, feel free to contribute as you see fit.
89 |
--------------------------------------------------------------------------------
/intentrace-example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sectordistrict/intentrace/93f34fe853b980974a735cfd4f730bcb67fd0910/intentrace-example.jpg
--------------------------------------------------------------------------------
/intentrace-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sectordistrict/intentrace/93f34fe853b980974a735cfd4f730bcb67fd0910/intentrace-example.png
--------------------------------------------------------------------------------
/itrace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sectordistrict/intentrace/93f34fe853b980974a735cfd4f730bcb67fd0910/itrace.png
--------------------------------------------------------------------------------
/src/auxiliary.rs:
--------------------------------------------------------------------------------
1 | pub mod constants {
2 | pub mod sizes {
3 | pub const SIGSET_SIZE: usize = size_of::();
4 | pub const TIMESPEC_SIZE: usize = size_of::();
5 | pub const CLONE3_ARGS_SIZE: usize = size_of::();
6 | pub const SIGACTION_SIZE: usize = size_of::();
7 | pub const RLIMIT_SIZE: usize = size_of::();
8 | }
9 | pub mod general {
10 | pub const MAX_KERNEL_ULONG: usize = unsafe { std::mem::transmute::(-4095) };
11 | // syscall: arch_prctl
12 | pub const ARCH_SET_GS: i32 = 0x1001;
13 | pub const ARCH_SET_FS: i32 = 0x1002;
14 | pub const ARCH_GET_FS: i32 = 0x1003;
15 | pub const ARCH_GET_GS: i32 = 0x1004;
16 | pub const ARCH_GET_CPUID: i32 = 0x1011;
17 | pub const ARCH_SET_CPUID: i32 = 0x1012;
18 | // syscall: landlock_add_rule
19 | pub const LANDLOCK_RULE_PATH_BENEATH: i32 = 1;
20 | }
21 | }
22 |
23 | pub mod kernel_errno {
24 | // kernel side errnos, not visible to userland
25 | use nix::libc::c_int;
26 | pub const ERESTARTSYS: c_int = 512;
27 | pub const ERESTARTNOINTR: c_int = 513;
28 | pub const ERESTARTNOHAND: c_int = 514; /* restart if no handler.. */
29 | pub const ERESTART_RESTARTBLOCK: c_int = 516; /* restart by calling sys_restart_syscall */
30 | pub const ENOIOCTLCMD: c_int = 515; /* No ioctl command */
31 | pub const EPROBE_DEFER: c_int = 517; /* Driver requests probe retry */
32 | pub const EOPENSTALE: c_int = 518; /* open found a stale dentry */
33 | pub const ENOPARAM: c_int = 519; /* Parameter not supported */
34 | /* Defined for the NFSv3 protocol */
35 | pub const EBADHANDLE: c_int = 521; /* Illegal NFS file handle */
36 | pub const ENOTSYNC: c_int = 522; /* Update synchronization mismatch */
37 | pub const EBADCOOKIE: c_int = 523; /* Cookie is stale */
38 | pub const ENOTSUPP: c_int = 524; /* Operation is not supported */
39 | pub const ETOOSMALL: c_int = 525; /* Buffer or request is too small */
40 | pub const ESERVERFAULT: c_int = 526; /* An untranslatable error occurred */
41 | pub const EBADTYPE: c_int = 527; /* Type not supported by server */
42 | pub const EJUKEBOX: c_int = 528; /* Request initiated, but will not complete before timeout */
43 | pub const EIOCBQUEUED: c_int = 529; /* iocb queued, will get completion event */
44 | pub const ERECALLCONFLICT: c_int = 530; /* conflict with recalled state */
45 | pub const ENOGRACE: c_int = 531; /* NFS file lock reclaim refused */
46 |
47 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
48 | #[repr(i32)]
49 | pub enum KernelErrno {
50 | UnknownErrno = 0,
51 | ERESTARTSYS = ERESTARTSYS,
52 | ERESTARTNOINTR = ERESTARTNOINTR,
53 | ERESTARTNOHAND = ERESTARTNOHAND,
54 | ERESTART_RESTARTBLOCK = ERESTART_RESTARTBLOCK,
55 | ENOIOCTLCMD = ENOIOCTLCMD,
56 | EPROBE_DEFER = EPROBE_DEFER,
57 | EOPENSTALE = EOPENSTALE,
58 | ENOPARAM = ENOPARAM,
59 | EBADHANDLE = EBADHANDLE,
60 | ENOTSYNC = ENOTSYNC,
61 | EBADCOOKIE = EBADCOOKIE,
62 | ENOTSUPP = ENOTSUPP,
63 | ETOOSMALL = ETOOSMALL,
64 | ESERVERFAULT = ESERVERFAULT,
65 | EBADTYPE = EBADTYPE,
66 | EJUKEBOX = EJUKEBOX,
67 | EIOCBQUEUED = EIOCBQUEUED,
68 | ERECALLCONFLICT = ERECALLCONFLICT,
69 | ENOGRACE = ENOGRACE,
70 | }
71 |
72 | impl std::fmt::Display for KernelErrno {
73 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
74 | write!(f, "{:?}: {}", self, self.desc())
75 | }
76 | }
77 |
78 | impl KernelErrno {
79 | pub fn from_i32(errno: i32) -> KernelErrno {
80 | match errno {
81 | ERESTARTSYS => KernelErrno::ERESTARTSYS,
82 | ERESTARTNOINTR => KernelErrno::ERESTARTNOINTR,
83 | ERESTARTNOHAND => KernelErrno::ERESTARTNOHAND,
84 | ERESTART_RESTARTBLOCK => KernelErrno::ERESTART_RESTARTBLOCK,
85 | ENOIOCTLCMD => KernelErrno::ENOIOCTLCMD,
86 | EPROBE_DEFER => KernelErrno::EPROBE_DEFER,
87 | EOPENSTALE => KernelErrno::EOPENSTALE,
88 | ENOPARAM => KernelErrno::ENOPARAM,
89 | EBADHANDLE => KernelErrno::EBADHANDLE,
90 | ENOTSYNC => KernelErrno::ENOTSYNC,
91 | EBADCOOKIE => KernelErrno::EBADCOOKIE,
92 | ENOTSUPP => KernelErrno::ENOTSUPP,
93 | ETOOSMALL => KernelErrno::ETOOSMALL,
94 | ESERVERFAULT => KernelErrno::ESERVERFAULT,
95 | EBADTYPE => KernelErrno::EBADTYPE,
96 | EJUKEBOX => KernelErrno::EJUKEBOX,
97 | EIOCBQUEUED => KernelErrno::EIOCBQUEUED,
98 | ERECALLCONFLICT => KernelErrno::ERECALLCONFLICT,
99 | ENOGRACE => KernelErrno::ENOGRACE,
100 | _ => KernelErrno::UnknownErrno,
101 | }
102 | }
103 |
104 | pub fn desc(&self) -> &'static str {
105 | // TODO!
106 | // these messages dont communicate EINTR conversion semantics
107 | match self {
108 | KernelErrno::UnknownErrno => "Unknown errno",
109 | // interrupted syscalls
110 |
111 | // ERESTARTSYS
112 | // always restart
113 | // except if a handler was registered without SA_RESTART, then convert to EINTR
114 | KernelErrno::ERESTARTSYS => {
115 | "Interrupted by a signal, restart if it has no handler or a SA_RESTART handler exists"
116 | }
117 |
118 | // ERESTARTNOINTR
119 | // always restart
120 | KernelErrno::ERESTARTNOINTR => "Interrupted by a signal, restart always",
121 |
122 | // ERESTARTNOHAND
123 | // always restart
124 | // except if a handler was registered, then convert to EINTR
125 | KernelErrno::ERESTARTNOHAND => {
126 | "Interrupted by a signal, restart if it has no handler"
127 | }
128 |
129 | // ERESTART_RESTARTBLOCK
130 | // should be restarted using a custom function.
131 | KernelErrno::ERESTART_RESTARTBLOCK => {
132 | "Interrupted by a signal, restart by calling restart_syscall"
133 | }
134 | //
135 | //
136 | //
137 | //
138 | KernelErrno::ENOIOCTLCMD => "No ioctl command",
139 | // if a driver depends on resources that are not yet available
140 | KernelErrno::EPROBE_DEFER => "Driver requests probe retry",
141 | KernelErrno::EOPENSTALE => "Open found a stale dentry",
142 | KernelErrno::ENOPARAM => "Parameter not supported",
143 | KernelErrno::EBADHANDLE => "Illegal NFS file handle",
144 | KernelErrno::ENOTSYNC => "Update synchronization mismatch",
145 | KernelErrno::EBADCOOKIE => "Cookie is stale",
146 | KernelErrno::ENOTSUPP => "Operation is not supported",
147 | KernelErrno::ETOOSMALL => "Buffer or request is too small",
148 | KernelErrno::ESERVERFAULT => "An untranslatable error occurred",
149 | KernelErrno::EBADTYPE => "Type not supported by server",
150 | KernelErrno::EJUKEBOX => "Request initiated, but will not complete before timeout",
151 | KernelErrno::EIOCBQUEUED => "iocb queued, will get completion event",
152 | KernelErrno::ERECALLCONFLICT => "conflict with recalled state",
153 | KernelErrno::ENOGRACE => "NFS file lock reclaim refused",
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/cli.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | path::{Path, PathBuf},
3 | str::FromStr,
4 | sync::{
5 | atomic::{AtomicBool, AtomicUsize},
6 | LazyLock,
7 | },
8 | };
9 |
10 | // TODO! Time blocks feature
11 | // pub static TIME_BLOCKS: Cell = Cell::new(false);
12 | pub static FOLLOW_FORKS: LazyLock = LazyLock::new(|| INTENTRACE_ARGS.follow_forks);
13 | pub static STRING_LIMIT: AtomicUsize = AtomicUsize::new(36);
14 | pub static FAILED_ONLY: LazyLock = LazyLock::new(|| INTENTRACE_ARGS.failed_only);
15 | pub static QUIET: LazyLock = LazyLock::new(|| INTENTRACE_ARGS.mute_stdout);
16 | pub static ANNOT: AtomicBool = AtomicBool::new(false);
17 | pub static ATTACH_PID: LazyLock> = LazyLock::new(|| INTENTRACE_ARGS.pid);
18 | pub static SUMMARY_ONLY: LazyLock = LazyLock::new(|| INTENTRACE_ARGS.summary_only);
19 | pub static SUMMARY: LazyLock = LazyLock::new(|| INTENTRACE_ARGS.summary);
20 | pub static SYSCALLS_TO_TRACE: LazyLock = LazyLock::new(|| {
21 | if INTENTRACE_ARGS.trace.is_empty() {
22 | SysnoSet::all()
23 | } else {
24 | let mut sysno_set = SysnoSet::empty();
25 | for syscall in INTENTRACE_ARGS.trace.iter() {
26 | match Sysno::from_str(syscall) {
27 | Ok(sysno) => {
28 | sysno_set.insert(sysno);
29 | }
30 | Err(_) => {
31 | eprintln!("Invalid syscall: {}", syscall);
32 | std::process::exit(100);
33 | }
34 | }
35 | }
36 | sysno_set
37 | }
38 | });
39 | pub static OUTPUT_FILE: LazyLock> = LazyLock::new(|| {
40 | INTENTRACE_ARGS
41 | .file
42 | .as_ref()
43 | .map(|pathbuf| pathbuf.as_path())
44 | });
45 | //
46 | pub static INTENTRACE_ARGS: LazyLock = LazyLock::new(IntentraceArgs::parse);
47 | pub static BINARY_AND_ARGS: LazyLock> =
48 | LazyLock::new(|| match INTENTRACE_ARGS.binary {
49 | Some(Binary::Command(ref regs)) if !regs.is_empty() => Some(regs),
50 | _ => None,
51 | });
52 |
53 | use clap::{Parser, Subcommand};
54 | use syscalls::{Sysno, SysnoSet};
55 |
56 | #[derive(Parser)]
57 | #[command(
58 | about = "intentrace is a strace for everyone.",
59 | version,
60 | allow_external_subcommands = true
61 | )]
62 | pub struct IntentraceArgs {
63 | /// print a summary table
64 | #[arg(short = 'c', long, conflicts_with = "summary")]
65 | pub summary_only: bool,
66 |
67 | /// print a summary table in addition to the normal output
68 | #[arg(short = 'C', long)]
69 | pub summary: bool,
70 |
71 | /// attach to an already running proceess
72 | #[arg(short = 'p', long = "attach")]
73 | pub pid: Option,
74 |
75 | /// redirect intentrace's output to a provided file
76 | #[arg(short = 'o', long = "output")]
77 | pub file: Option,
78 |
79 | /// trace a specific syscall or a group of syscalls delimited by ','
80 | #[arg(long, value_delimiter = ',')]
81 | pub trace: Vec,
82 |
83 | /// trace child processes when traced programs create them
84 | #[arg(
85 | short = 'f',
86 | long = "follow-forks",
87 | conflicts_with = "pid",
88 | conflicts_with = "failed_only"
89 | )]
90 | pub follow_forks: bool,
91 |
92 | /// only print failed syscalls
93 | #[arg(short = 'Z', long = "failed-only")]
94 | pub failed_only: bool,
95 |
96 | /// mute the traced program's std output
97 | #[arg(short = 'q', long = "mute-stdout")]
98 | pub mute_stdout: bool,
99 |
100 | #[command(subcommand)]
101 | pub binary: Option,
102 | }
103 |
104 | #[derive(Subcommand, Debug, PartialEq)]
105 | pub enum Binary {
106 | #[command(external_subcommand)]
107 | Command(Vec),
108 | }
109 |
--------------------------------------------------------------------------------
/src/colors.rs:
--------------------------------------------------------------------------------
1 | use colored::CustomColor;
2 | use std::sync::{LazyLock, Mutex};
3 |
4 | pub static TERMINAL_THEME: LazyLock = LazyLock::new(|| {
5 | termbg::theme(std::time::Duration::from_millis(10)).unwrap_or(termbg::Theme::Dark)
6 | });
7 |
8 | pub static PAGES_COLOR: LazyLock =
9 | LazyLock::new(|| from_terminal_theme((0, 169, 233), (0, 169, 223)));
10 | pub static GENERAL_TEXT_COLOR: LazyLock =
11 | LazyLock::new(|| from_terminal_theme((64, 64, 64), (160, 160, 160)));
12 | pub static PID_BACKGROUND_COLOR: LazyLock =
13 | LazyLock::new(|| from_terminal_theme((146, 146, 168), (0, 0, 0)));
14 | pub static PID_NUMBER_COLOR: LazyLock =
15 | LazyLock::new(|| from_terminal_theme((0, 0, 140), (0, 173, 216)));
16 | pub static EXITED_BACKGROUND_COLOR: LazyLock =
17 | LazyLock::new(|| from_terminal_theme((250, 160, 160), (100, 0, 0)));
18 | pub static OUR_YELLOW: LazyLock =
19 | LazyLock::new(|| from_terminal_theme((112, 127, 35), (187, 142, 35)));
20 | pub static CONTINUED_COLOR: LazyLock =
21 | LazyLock::new(|| from_terminal_theme((188, 210, 230), (17, 38, 21)));
22 | pub static STOPPED_COLOR: LazyLock =
23 | LazyLock::new(|| from_terminal_theme((82, 138, 174), (47, 86, 54)));
24 | // TODO!
25 | // find a good alternate color for light mode terminals
26 | pub static PARTITION_1_COLOR: LazyLock =
27 | LazyLock::new(|| from_terminal_theme((112, 127, 35), (186, 171, 35)));
28 | pub static PARTITION_2_COLOR: LazyLock<[CustomColor; 3]> =
29 | LazyLock::new(|| match *TERMINAL_THEME {
30 | termbg::Theme::Light => [CustomColor::new(0, 169, 223), CustomColor::new(0, 169, 223), CustomColor::new(0, 169, 223)],
31 | termbg::Theme::Dark => [CustomColor::new(0, 169, 233), CustomColor::new(92, 92, 255), CustomColor::new(0, 218, 233)],
32 | });
33 | pub static PATHLIKE_ALTERNATOR: LazyLock> = LazyLock::new(|| Mutex::new(0));
34 |
35 | fn from_terminal_theme(
36 | (light_R, light_G, light_B): (u8, u8, u8),
37 | (dark_R, dark_G, dark_B): (u8, u8, u8),
38 | ) -> CustomColor {
39 | match *TERMINAL_THEME {
40 | termbg::Theme::Light => CustomColor::new(light_R, light_G, light_B),
41 | termbg::Theme::Dark => CustomColor::new(dark_R, dark_G, dark_B),
42 | }
43 | }
44 |
45 | pub fn switch_pathlike_color() {
46 | let prev = *PATHLIKE_ALTERNATOR.lock().unwrap();
47 | *PATHLIKE_ALTERNATOR.lock().unwrap() = (prev + 1) % 3;
48 | }
49 |
--------------------------------------------------------------------------------
/src/macro_interps.rs:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | #![allow(
2 | unused_doc_comments,
3 | dead_code,
4 | non_camel_case_types,
5 | unused_macros,
6 | non_snake_case,
7 | invalid_value,
8 | unused_assignments
9 | )]
10 |
11 | macro_rules! p {
12 | ($a:expr) => {
13 | println!("{:?}", $a)
14 | };
15 | }
16 | macro_rules! pp {
17 | ($a:expr, $b:expr) => {
18 | println!("{:?}, {:?}", $a, $b)
19 | };
20 | }
21 | macro_rules! ppp {
22 | ($a:expr, $b:expr, $c:expr) => {
23 | println!("{:?}, {:?}, {:?}", $a, $b, $c)
24 | };
25 | }
26 |
27 | use std::{
28 | collections::HashMap,
29 | mem::{self},
30 | os::unix::process::CommandExt,
31 | process::{exit, Command, Stdio},
32 | sync::atomic::Ordering,
33 | time::Duration,
34 | };
35 |
36 | use clap::Parser;
37 | use cli::{
38 | IntentraceArgs, ATTACH_PID, BINARY_AND_ARGS, FAILED_ONLY, FOLLOW_FORKS, QUIET, SUMMARY,
39 | SUMMARY_ONLY,
40 | };
41 | use colored::Colorize;
42 | use colors::{GENERAL_TEXT_COLOR, STOPPED_COLOR};
43 | use nix::{
44 | errno::Errno,
45 | sys::{
46 | ptrace::{self},
47 | wait::waitpid,
48 | },
49 | unistd::{fork, ForkResult::*, Pid},
50 | };
51 | use pete::{Ptracer, Restart, Stop};
52 | use syscalls::Sysno;
53 | use utilities::{
54 | interpret_syscall_result, set_memory_break_pre_call, syscall_is_blocking, HALT_TRACING, REGISTERS,
55 | TABLE, TABLE_FOLLOW_FORKS,
56 | };
57 | use writer::{
58 | empty_buffer, flush_buffer, initialize_writer, write_exiting, write_syscall_not_covered,
59 | write_text,
60 | };
61 |
62 | mod syscall_categories;
63 | mod syscall_object;
64 | // mod syscall_annotations_map;
65 | // mod syscall_object_annotations;
66 | mod syscall_skeleton_map;
67 | mod types;
68 | use syscall_object::{SyscallObject, SyscallResult, SyscallState};
69 | mod auxiliary;
70 | mod cli;
71 | mod colors;
72 | mod one_line_formatter;
73 | mod peeker_poker;
74 | mod return_resolvers;
75 | mod utilities;
76 | mod writer;
77 |
78 | fn main() -> anyhow::Result<()> {
79 | IntentraceArgs::parse();
80 | initialize_writer();
81 | ctrlc::set_handler(|| {
82 | flush_buffer();
83 | HALT_TRACING.store(true, Ordering::SeqCst);
84 | if *SUMMARY_ONLY || *SUMMARY {
85 | print_table();
86 | }
87 | std::process::exit(0);
88 | })?;
89 | if *FOLLOW_FORKS {
90 | follow_forks(*BINARY_AND_ARGS)
91 | } else {
92 | match *ATTACH_PID {
93 | Some(pid) => {
94 | let child = Pid::from_raw(pid as i32);
95 | ptrace::attach(child)?;
96 | parent(child);
97 | }
98 | None => match *BINARY_AND_ARGS {
99 | Some(binary_and_args) => match unsafe { fork() }? {
100 | Parent { child } => {
101 | parent(child);
102 | }
103 | Child => child_trace_me(binary_and_args),
104 | },
105 | None => {
106 | eprintln!("Usage: must provide a command to run or attach to a PID\n");
107 | exit(100);
108 | }
109 | },
110 | }
111 | }
112 | if !*FAILED_ONLY {
113 | // flush_buffer();
114 | }
115 | if *SUMMARY_ONLY || *SUMMARY {
116 | print_table();
117 | }
118 | Ok(())
119 | }
120 |
121 | fn child_trace_me(comm: &[String]) {
122 | let mut command = Command::new(&comm[0]);
123 | command.args(&comm[1..]);
124 |
125 | if *QUIET {
126 | command.stdout(Stdio::null());
127 | }
128 |
129 | ptrace::traceme().unwrap();
130 | let res = command.exec();
131 |
132 | // unreachable unless exec fails
133 | eprintln!("Could not execute program");
134 | std::process::exit(res.raw_os_error().unwrap())
135 | }
136 |
137 | fn follow_forks(command_to_run: Option<&[String]>) {
138 | match command_to_run {
139 | Some(comm) => {
140 | let mut command = Command::new(&comm[0]);
141 | command.args(&comm[1..]);
142 |
143 | if *QUIET {
144 | command.stdout(Stdio::null());
145 | }
146 |
147 | let mut ptracer = Ptracer::new();
148 | *ptracer.poll_delay_mut() = Duration::from_nanos(1);
149 | ptracer.spawn(command).unwrap();
150 | ptrace_ptracer(ptracer);
151 | }
152 | None => {
153 | eprintln!("Usage: must provide a command to run\n");
154 | exit(100);
155 | }
156 | }
157 | }
158 |
159 | fn parent(tracee_pid: Pid) {
160 | // skip first execve
161 | let _res = waitpid(tracee_pid, None).unwrap();
162 | let mut syscall_entering = true;
163 | let (mut start, mut end) = (None, None);
164 | let mut syscall = SyscallObject::default();
165 | let (mut supported, mut skip) = (true, false);
166 | 'main_loop: loop {
167 | match ptrace::syscall(tracee_pid, None) {
168 | Ok(_void) => {
169 | let _res = waitpid(tracee_pid, None).expect("Failed waiting for child.");
170 | match syscall_entering {
171 | true => {
172 | empty_buffer();
173 | // SYSCALL ABOUT TO RUN
174 | let ptrace_regs = nix::sys::ptrace::getregs(tracee_pid);
175 | if let Err(Errno::ESRCH) = ptrace_regs {
176 | write_exiting(tracee_pid);
177 | break 'main_loop;
178 | }
179 | let registers = ptrace_regs.unwrap();
180 | let sysno = Sysno::from(registers.orig_rax as i32);
181 | skip = SyscallObject::should_skip_building(sysno);
182 | if !skip {
183 | if let Some(syscall_built) = SyscallObject::build(tracee_pid, sysno) {
184 | syscall = syscall_built;
185 | *REGISTERS.lock().unwrap() = [
186 | registers.rdi,
187 | registers.rsi,
188 | registers.rdx,
189 | registers.r10,
190 | registers.r8,
191 | registers.r9,
192 | ];
193 | syscall_will_run(&mut syscall);
194 | if *SUMMARY {
195 | start = Some(std::time::Instant::now());
196 | }
197 | } else {
198 | write_syscall_not_covered(sysno, tracee_pid);
199 | supported = false;
200 | }
201 | }
202 | syscall_entering = false;
203 | if *SUMMARY_ONLY {
204 | start = Some(std::time::Instant::now());
205 | }
206 | continue 'main_loop;
207 | }
208 | false => {
209 | // SYSCALL RETURNED
210 | end = Some(std::time::Instant::now());
211 | let ptrace_regs = nix::sys::ptrace::getregs(tracee_pid);
212 | if let Err(Errno::ESRCH) = ptrace_regs {
213 | write_exiting(tracee_pid);
214 | break 'main_loop;
215 | }
216 | let registers = ptrace_regs.unwrap();
217 | let sysno = Sysno::from(registers.orig_rax as i32);
218 | if supported && !skip {
219 | *REGISTERS.lock().unwrap() = [
220 | registers.rdi,
221 | registers.rsi,
222 | registers.rdx,
223 | registers.r10,
224 | registers.r8,
225 | registers.r9,
226 | ];
227 | syscall_returned(&mut syscall, registers.rax);
228 | if *SUMMARY {
229 | let mut table = TABLE.lock().unwrap();
230 | table
231 | .entry(sysno)
232 | .and_modify(|(count, duration, errors)| {
233 | *count += 1;
234 | *duration = duration.saturating_add(
235 | end.unwrap().duration_since(start.unwrap()),
236 | );
237 | *errors += syscall.has_errored() as usize;
238 | })
239 | .or_insert((
240 | 1,
241 | end.unwrap().duration_since(start.unwrap()),
242 | syscall.has_errored() as usize,
243 | ));
244 | start = None;
245 | end = None;
246 | }
247 | }
248 | if *SUMMARY_ONLY {
249 | let mut table = TABLE.lock().unwrap();
250 | let syscall_result = interpret_syscall_result(registers.rax);
251 | let errored = matches!(syscall_result, SyscallResult::Fail(_));
252 | table
253 | .entry(sysno)
254 | .and_modify(|(count, duration, errors)| {
255 | *count += 1;
256 | *duration = duration.saturating_add(
257 | end.unwrap().duration_since(start.unwrap()),
258 | );
259 | *errors += errored as usize;
260 | })
261 | .or_insert((
262 | 1,
263 | end.unwrap().duration_since(start.unwrap()),
264 | errored as usize,
265 | ));
266 | start = None;
267 | end = None;
268 | }
269 | supported = true;
270 | skip = false;
271 | syscall_entering = true;
272 | }
273 | }
274 | }
275 | Err(errno) => {
276 | if errno == Errno::ESRCH {
277 | eprintln!("\n\nTracee died\nlast syscall: {}", syscall.sysno);
278 | } else {
279 | eprintln!("\n\nError: {errno}\nlast syscall: {}", syscall.sysno);
280 | }
281 | break 'main_loop;
282 | }
283 | }
284 | }
285 | }
286 |
287 | fn ptrace_ptracer(mut ptracer: Ptracer) {
288 | let mut last_sysno: syscalls::Sysno = unsafe { mem::zeroed() };
289 | let mut last_pid = unsafe { mem::zeroed() };
290 | let mut pid_syscall_map: HashMap = HashMap::new();
291 |
292 | while let Ok(Some(tracee)) = ptracer.wait() {
293 | if HALT_TRACING.load(Ordering::SeqCst) {
294 | break;
295 | }
296 | let tracee_pid = Pid::from_raw(tracee.pid.as_raw());
297 | match tracee.stop {
298 | Stop::SyscallEnter => {
299 | let ptrace_regs = nix::sys::ptrace::getregs(tracee_pid);
300 | if let Err(errno) = ptrace_regs {
301 | handle_getting_registers_error(errno, "enter", last_sysno)
302 | }
303 | let registers = ptrace_regs.unwrap();
304 |
305 | check_syscall_switch(last_pid, tracee_pid, &mut pid_syscall_map);
306 | let sysno = Sysno::from(registers.orig_rax as i32);
307 | last_sysno = sysno;
308 | if !SyscallObject::should_skip_building(sysno) {
309 | let syscall_built = SyscallObject::build(tracee_pid, sysno);
310 | if let Some(mut syscall) = syscall_built {
311 | *REGISTERS.lock().unwrap() = [
312 | registers.rdi,
313 | registers.rsi,
314 | registers.rdx,
315 | registers.r10,
316 | registers.r8,
317 | registers.r9,
318 | ];
319 | syscall_will_run(&mut syscall);
320 | syscall.state = SyscallState::Exiting;
321 | pid_syscall_map.insert(tracee_pid, syscall);
322 | if *SUMMARY {
323 | let mut table = TABLE_FOLLOW_FORKS.lock().unwrap();
324 | table
325 | .entry(sysno)
326 | .and_modify(|value| {
327 | *value += 1;
328 | })
329 | .or_insert(1);
330 | }
331 | } else {
332 | write_syscall_not_covered(sysno, tracee_pid);
333 | }
334 | }
335 | if *SUMMARY_ONLY {
336 | let mut table = TABLE_FOLLOW_FORKS.lock().unwrap();
337 | table
338 | .entry(sysno)
339 | .and_modify(|value| {
340 | *value += 1;
341 | })
342 | .or_insert(1);
343 | }
344 | last_pid = tracee_pid;
345 | }
346 | Stop::SyscallExit => {
347 | check_syscall_switch(last_pid, tracee_pid, &mut pid_syscall_map);
348 | let ptrace_regs = nix::sys::ptrace::getregs(tracee_pid);
349 | if let Err(errno) = ptrace_regs {
350 | handle_getting_registers_error(errno, "enter", last_sysno)
351 | }
352 | let registers = ptrace_regs.unwrap();
353 | *REGISTERS.lock().unwrap() = [
354 | registers.rdi,
355 | registers.rsi,
356 | registers.rdx,
357 | registers.r10,
358 | registers.r8,
359 | registers.r9,
360 | ];
361 | if let Some(syscall) = pid_syscall_map.get_mut(&tracee_pid) {
362 | syscall_returned(syscall, registers.rax);
363 | pid_syscall_map.remove(&tracee_pid).unwrap();
364 | }
365 | last_pid = tracee_pid;
366 | }
367 | _ => {}
368 | }
369 | match ptracer.restart(tracee, Restart::Syscall) {
370 | Ok(_) => {}
371 | Err(e) => {
372 | if let pete::Error::TraceeDied { pid, source } = e {
373 | if source as i32 == Errno::ESRCH as i32 {
374 | write_exiting(Pid::from_raw(pid.as_raw()));
375 | return;
376 | }
377 | }
378 | eprintln!("{}", e);
379 | }
380 | };
381 | }
382 | }
383 |
384 | fn syscall_will_run(syscall: &mut SyscallObject) {
385 | // handle program break point
386 | if syscall.is_mem_alloc_dealloc() {
387 | set_memory_break_pre_call(syscall.tracee_pid);
388 | }
389 | match *FOLLOW_FORKS {
390 | true => {
391 | syscall.fill_buffer();
392 | flush_buffer();
393 | empty_buffer();
394 | }
395 | false => {
396 | syscall.state = SyscallState::Entering;
397 | syscall.fill_buffer();
398 | }
399 | }
400 | }
401 |
402 | fn syscall_returned(syscall: &mut SyscallObject, return_value: u64) {
403 | syscall.result = interpret_syscall_result(return_value);
404 |
405 | match *FOLLOW_FORKS {
406 | true => {
407 | syscall.fill_buffer();
408 | write_text("\n".white());
409 | }
410 | false => {
411 | if *FAILED_ONLY && !syscall.has_errored() {
412 | empty_buffer();
413 | return;
414 | }
415 | syscall.state = SyscallState::Exiting;
416 | syscall.fill_buffer();
417 | // this line was moved from the main loops to after this check ^
418 | // it was previously not aware of FAILED_ONLY being its edge-case
419 | // this resulted in long streaks of newlines in the output
420 | // this is also more correct
421 | write_text("\n".white());
422 | }
423 | }
424 | if syscall.currently_blocking {
425 | write_text(syscall_is_blocking());
426 | }
427 | flush_buffer();
428 | empty_buffer();
429 | }
430 |
431 | fn check_syscall_switch(
432 | last_pid: Pid,
433 | syscall_pid: Pid,
434 | pid_syscall_map: &mut HashMap,
435 | ) {
436 | if syscall_pid != last_pid {
437 | if let Some(last_syscall) = pid_syscall_map.get_mut(&last_pid) {
438 | if !last_syscall.is_exiting() {
439 | last_syscall.paused = true;
440 | write_text(" ├ ".custom_color(*GENERAL_TEXT_COLOR));
441 | write_text(" STOPPED ".on_custom_color(*STOPPED_COLOR));
442 | write_text("\n".white());
443 | }
444 | }
445 | }
446 | }
447 |
448 | fn handle_getting_registers_error(errno: Errno, syscall_enter_or_exit: &str, sysno: Sysno) {
449 | if sysno == Sysno::exit || sysno == Sysno::exit_group {
450 | // no longer needed
451 | // println!("\n\nSuccessfully exited\n");
452 | } else {
453 | match errno {
454 | Errno::ESRCH => {
455 | println!(
456 | "\n\n getting registers: syscall-{syscall_enter_or_exit} error: process \
457 | disappeared\nsyscall: {sysno}, error: {errno}"
458 | );
459 | exit(0);
460 | }
461 | _ => println!("Encountered error while retrieving registers"),
462 | }
463 | }
464 | }
465 |
466 | fn print_table() {
467 | if *FOLLOW_FORKS {
468 | let output = TABLE_FOLLOW_FORKS.lock().unwrap();
469 | let mut vec = Vec::from_iter(output.iter());
470 | vec.sort_by(|(_sysno, count), (_sysno2, count2)| count2.cmp(count));
471 |
472 | use tabled::{builder::Builder, settings::Style};
473 | let mut builder = Builder::new();
474 |
475 | builder.push_record(["calls", "syscall"]);
476 | builder.push_record([""]);
477 | for (sys, count) in vec {
478 | builder.push_record([&count.to_string(), sys.name()]);
479 | }
480 | let table = builder.build().with(Style::ascii_rounded()).to_string();
481 |
482 | eprintln!("\n{}", table);
483 | } else {
484 | let output = TABLE.lock().unwrap();
485 | let mut vec = Vec::from_iter(output.iter());
486 | vec.sort_by(|(_, (_, duration, _)), (_, (_, duration2, _))| duration2.cmp(duration));
487 |
488 | use tabled::{builder::Builder, settings::Style};
489 | let mut builder = Builder::new();
490 |
491 | builder.push_record([
492 | "% time",
493 | "seconds",
494 | "usecs/call",
495 | "calls",
496 | "errors",
497 | "syscall",
498 | ]);
499 | builder.push_record([""]);
500 | let total_time = vec
501 | .iter()
502 | .map(|(_, (_, time, _))| time.as_micros())
503 | .sum::();
504 | let empty_string = "".to_owned();
505 | for (sys, (count, time, errors)) in vec {
506 | let time_MICROS = time.as_micros() as f64;
507 | let time = time_MICROS / 1_000_000.0;
508 | let usecs_call = (time_MICROS / *count as f64) as i64;
509 | let time_percent = time_MICROS / total_time as f64;
510 | let errors = if *errors == 0 {
511 | &empty_string
512 | } else {
513 | &errors.to_string()
514 | };
515 | builder.push_record([
516 | &format!("{:.2}", time_percent * 100.0),
517 | &format!("{:.6}", time),
518 | &format!("{}", usecs_call),
519 | &count.to_string(),
520 | errors,
521 | sys.name(),
522 | ]);
523 | }
524 | let table = builder.build().with(Style::ascii_rounded()).to_string();
525 |
526 | eprintln!("\n{}", table);
527 | }
528 | }
529 |
--------------------------------------------------------------------------------
/src/peeker_poker.rs:
--------------------------------------------------------------------------------
1 | // file is called peeker poker
2 | // because it's the lingo[1] that ptrace
3 | // uses to refer to reading/writing
4 | // from/to a tracee's memory
5 | //
6 | // [1]: https://en.wikipedia.org/wiki/PEEK_and_POKE
7 | //
8 | #[cfg(target_pointer_width = "32")]
9 | pub const WORD_SIZE: usize = 4;
10 |
11 | #[cfg(target_pointer_width = "64")]
12 | pub const WORD_SIZE: usize = 8;
13 |
14 | use std::{ffi::c_void, io::IoSliceMut, num::NonZeroUsize};
15 |
16 | use nix::{
17 | libc::{cpu_set_t, CPU_ISSET, CPU_SETSIZE},
18 | sys::{
19 | ptrace,
20 | uio::{process_vm_readv, RemoteIoVec},
21 | },
22 | unistd::Pid,
23 | };
24 |
25 | pub fn read_bytes(addr: usize, pid: Pid) -> Option<[u8; N]> {
26 | let base = addr;
27 | let remote_iov = RemoteIoVec { base, len: N };
28 | // TODO!
29 | // large array sizes might overflow
30 | let mut bytes_buffer = [0u8; N];
31 | process_vm_readv(
32 | pid,
33 | &mut [IoSliceMut::new(&mut bytes_buffer)],
34 | &[remote_iov],
35 | ).ok()?;
36 | Some(bytes_buffer)
37 | }
38 |
39 | pub fn read_bytes_variable_length(base: usize, pid: Pid, len: usize) -> Option> {
40 | let remote_iov = RemoteIoVec { base, len };
41 | let mut bytes_buffer = vec![0u8; len];
42 | // Note, however, that these system calls
43 | // do not check the memory regions in the remote process
44 | // until just before doing the read/write.
45 | // Consequently, a partial read/write (see RETURN VALUE) may result
46 | // if one of the remote_iov elements points to an invalid memory region in the remote process.
47 | // No further reads/writes will be attempted beyond that point.
48 | //
49 | // Keep this in mind when attempting to read data of unknown length
50 | // (such as C strings that are null-terminated) from a remote process,
51 | // by avoiding spanning memory pages (typically 4 KiB)
52 | // in a single remote iovec element.
53 | // (Instead, split the remote read into two remote_iov elements
54 | // and have them merge back into a single write local_iov entry.
55 | // The first read entry goes up to the page boundary,
56 | let _ = process_vm_readv(
57 | pid,
58 | &mut [IoSliceMut::new(&mut bytes_buffer)],
59 | &[remote_iov],
60 | )
61 | .ok()?;
62 | Some(bytes_buffer)
63 | }
64 |
65 | pub fn read_bytes_as_struct(addr: usize, pid: Pid) -> Option {
66 | let vec = read_bytes::(addr, pid)?;
67 | Some(unsafe { std::mem::transmute_copy(&vec) })
68 | }
69 |
70 | pub fn read_one_word(address: usize, pid: Pid) -> Option {
71 | let remote_iov = RemoteIoVec {
72 | base: address,
73 | len: 1,
74 | };
75 | let mut bytes_buffer = vec![0u8; 4];
76 | let _ = process_vm_readv(
77 | pid,
78 | &mut [IoSliceMut::new(&mut bytes_buffer)],
79 | &[remote_iov],
80 | )
81 | .ok()?;
82 | Some(unsafe { std::mem::transmute(&bytes_buffer) })
83 | }
84 |
85 | pub fn read_bytes_until_null(address: usize, pid: Pid) -> Option> {
86 | let mut address = address as *mut c_void;
87 | let mut data = vec![];
88 | 'read_loop: loop {
89 | // TODO!
90 | // change this to be similar to read_words_until_null below
91 | // i.e. if err: return collected data so far
92 | let word = ptrace::read(pid, address).ok()?;
93 | let bytes: [u8; WORD_SIZE] = unsafe { std::mem::transmute(word) };
94 | for byte in bytes {
95 | if byte == b'\0' {
96 | break 'read_loop;
97 | }
98 | data.push(byte);
99 | }
100 | address = unsafe { address.byte_add(WORD_SIZE) };
101 | }
102 | Some(data)
103 | }
104 |
105 | // usually used to resolve array of pointers to *
106 | pub fn read_words_until_null(address: usize, pid: Pid) -> Option> {
107 | let mut addr = address as *mut c_void;
108 | let mut data = vec![];
109 | 'read_loop: loop {
110 | match ptrace::read(pid, addr) {
111 | Ok(word) => {
112 | if word == 0 {
113 | break 'read_loop;
114 | }
115 | data.push(word as usize);
116 | addr = unsafe { addr.byte_add(WORD_SIZE) };
117 | }
118 | Err(_err) => return Some(data),
119 | };
120 | }
121 | Some(data)
122 | }
123 |
124 | pub fn read_affinity_from_child(address: usize, pid: Pid) -> Option> {
125 | const CPU_SET_USIZE: usize = (CPU_SETSIZE / WORD_SIZE as i32) as usize;
126 |
127 | let cpu_set = read_bytes_as_struct::(address, pid)?;
128 |
129 | let mut vec = Vec::new();
130 | for cpu_number in 0..std::thread::available_parallelism()
131 | .map(NonZeroUsize::get)
132 | .unwrap_or(1) as usize
133 | {
134 | if unsafe { CPU_ISSET(cpu_number, &cpu_set) } {
135 | vec.push(cpu_number)
136 | }
137 | }
138 | Some(vec)
139 | }
140 |
141 | pub fn read_string_specific_length(addr: usize, pid: Pid, size: usize) -> Option {
142 | let bytes_buffer = read_bytes_variable_length(addr, pid, size)?;
143 | Some(String::from_utf8_lossy(&bytes_buffer).into_owned())
144 | }
145 |
146 | pub fn write_bytes(addr: usize, pid: Pid, data: [u64; N]) -> Result<(), ()> {
147 | let mut addr = addr as *mut c_void;
148 | for word in data {
149 | match ptrace::write(pid, addr, word as _) {
150 | Ok(_void) => {
151 | addr = unsafe { addr.byte_add(WORD_SIZE) };
152 | }
153 | Err(_res) => return Err(()),
154 | };
155 | }
156 | Ok(())
157 | }
158 |
--------------------------------------------------------------------------------
/src/return_resolvers.rs:
--------------------------------------------------------------------------------
1 | pub mod Readers_Writers {
2 | // basically all syscalls that return `ssize_t`
3 | pub fn parse_return(return_register: u64) -> String {
4 | format!("{return_register} Bytes")
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/syscall_categories.rs:
--------------------------------------------------------------------------------
1 | use crate::types::Category;
2 | use std::collections::HashMap;
3 | use syscalls::Sysno;
4 | // TODO!
5 | // revise and compare with strace's system
6 | pub fn initialize_categories_map() -> HashMap {
7 | use Category::*;
8 | let array: Vec<(Sysno, Category)> = vec![
9 | (Sysno::read, DiskIO),
10 | (Sysno::write, DiskIO),
11 | (Sysno::pread64, DiskIO),
12 | (Sysno::pwrite64, DiskIO),
13 | (Sysno::readv, DiskIO),
14 | (Sysno::writev, DiskIO),
15 | (Sysno::preadv, DiskIO),
16 | (Sysno::pwritev, DiskIO),
17 | (Sysno::preadv2, DiskIO),
18 | (Sysno::pwritev2, DiskIO),
19 | (Sysno::sync, DiskIO),
20 | (Sysno::syncfs, DiskIO),
21 | (Sysno::fsync, DiskIO),
22 | (Sysno::fdatasync, DiskIO),
23 | (Sysno::truncate, DiskIO),
24 | (Sysno::ftruncate, DiskIO),
25 | (Sysno::getdents, DiskIO),
26 | (Sysno::getdents64, DiskIO),
27 | (Sysno::pipe, DiskIO),
28 | (Sysno::pipe2, DiskIO),
29 | (Sysno::dup, FileOp),
30 | (Sysno::dup2, FileOp),
31 | (Sysno::dup3, FileOp),
32 | (Sysno::access, FileOp),
33 | (Sysno::faccessat, FileOp),
34 | (Sysno::faccessat2, FileOp),
35 | (Sysno::open, FileOp),
36 | (Sysno::openat, FileOp),
37 | (Sysno::openat2, FileOp),
38 | (Sysno::creat, FileOp),
39 | (Sysno::getcwd, FileOp),
40 | (Sysno::chdir, FileOp),
41 | (Sysno::fchdir, FileOp),
42 | (Sysno::rename, FileOp),
43 | (Sysno::renameat, FileOp),
44 | (Sysno::renameat2, FileOp),
45 | (Sysno::mkdir, FileOp),
46 | (Sysno::mkdirat, FileOp),
47 | (Sysno::link, FileOp),
48 | (Sysno::linkat, FileOp),
49 | (Sysno::unlink, FileOp),
50 | (Sysno::unlinkat, FileOp),
51 | (Sysno::rmdir, FileOp),
52 | (Sysno::symlink, FileOp),
53 | (Sysno::symlinkat, FileOp),
54 | (Sysno::readlink, FileOp),
55 | (Sysno::readlinkat, FileOp),
56 | (Sysno::chmod, FileOp),
57 | (Sysno::fchmod, FileOp),
58 | (Sysno::fchmodat, FileOp),
59 | (Sysno::chown, FileOp),
60 | (Sysno::fchown, FileOp),
61 | (Sysno::lchown, FileOp),
62 | (Sysno::fchownat, FileOp),
63 | (Sysno::close, FileOp),
64 | (Sysno::stat, FileOp),
65 | (Sysno::fstat, FileOp),
66 | (Sysno::lstat, FileOp),
67 | (Sysno::newfstatat, FileOp),
68 | (Sysno::statx, FileOp),
69 | (Sysno::statfs, FileOp),
70 | (Sysno::fstatfs, FileOp),
71 | (Sysno::lseek, FileOp),
72 | (Sysno::fallocate, FileOp),
73 | (Sysno::ustat, Device),
74 | (Sysno::cachestat, Memory),
75 | (Sysno::mmap, Memory),
76 | (Sysno::mprotect, Memory),
77 | (Sysno::munmap, Memory),
78 | (Sysno::brk, Memory),
79 | (Sysno::mlock, Memory),
80 | (Sysno::mlock2, Memory),
81 | (Sysno::munlock, Memory),
82 | (Sysno::mlockall, Memory),
83 | (Sysno::munlockall, Memory),
84 | (Sysno::mremap, Memory),
85 | (Sysno::msync, Memory),
86 | (Sysno::mincore, Memory),
87 | (Sysno::madvise, Memory),
88 | (Sysno::select, AsyncIO),
89 | (Sysno::pselect6, AsyncIO),
90 | (Sysno::poll, AsyncIO),
91 | (Sysno::ppoll, AsyncIO),
92 | (Sysno::epoll_create, AsyncIO),
93 | (Sysno::epoll_create1, AsyncIO),
94 | (Sysno::epoll_wait, AsyncIO),
95 | (Sysno::epoll_pwait, AsyncIO),
96 | (Sysno::epoll_pwait2, AsyncIO),
97 | (Sysno::epoll_ctl, AsyncIO),
98 | (Sysno::socket, Network),
99 | (Sysno::bind, Network),
100 | (Sysno::getsockname, Network),
101 | (Sysno::getpeername, Network),
102 | (Sysno::socketpair, Network),
103 | (Sysno::setsockopt, Network),
104 | (Sysno::getsockopt, Network),
105 | (Sysno::listen, Network),
106 | (Sysno::accept, Network),
107 | (Sysno::accept4, Network),
108 | (Sysno::connect, Network),
109 | (Sysno::sendto, Network),
110 | (Sysno::sendmsg, Network),
111 | (Sysno::recvfrom, Network),
112 | (Sysno::recvmsg, Network),
113 | (Sysno::shutdown, Process),
114 | (Sysno::fcntl, FileOp),
115 | (Sysno::ioctl, Device),
116 | (Sysno::getrandom, Device),
117 | (Sysno::rt_sigaction, Signals),
118 | (Sysno::rt_sigprocmask, Signals),
119 | (Sysno::rt_sigsuspend, Signals),
120 | (Sysno::sigaltstack, Signals),
121 | (Sysno::rt_sigreturn, Signals),
122 | (Sysno::rt_sigpending, Signals),
123 | (Sysno::rt_sigtimedwait, Signals),
124 | (Sysno::rt_sigqueueinfo, Signals),
125 | (Sysno::rt_tgsigqueueinfo, Signals),
126 | (Sysno::signalfd, Signals),
127 | (Sysno::signalfd4, Signals),
128 | (Sysno::kill, Signals),
129 | (Sysno::tgkill, Signals),
130 | (Sysno::tkill, Signals),
131 | (Sysno::pause, Signals),
132 | (Sysno::pidfd_send_signal, Signals),
133 | (Sysno::arch_prctl, Process),
134 | (Sysno::sched_yield, Process),
135 | (Sysno::gettid, Process),
136 | (Sysno::getpid, Process),
137 | (Sysno::getppid, Process),
138 | (Sysno::times, Process),
139 | (Sysno::setrlimit, Process),
140 | (Sysno::getrlimit, Process),
141 | (Sysno::prlimit64, Process),
142 | (Sysno::sched_setaffinity, Process),
143 | (Sysno::sched_getaffinity, Process),
144 | (Sysno::getuid, Process),
145 | (Sysno::geteuid, Process),
146 | (Sysno::getgid, Process),
147 | (Sysno::getegid, Process),
148 | (Sysno::setuid, Process),
149 | (Sysno::setgid, Process),
150 | (Sysno::setpgid, Process),
151 | (Sysno::getpgid, Process),
152 | (Sysno::getpgrp, Process),
153 | (Sysno::exit, Process),
154 | (Sysno::exit_group, Process),
155 | (Sysno::rseq, Process),
156 | (Sysno::set_tid_address, Process),
157 | (Sysno::wait4, Process),
158 | (Sysno::waitid, Process),
159 | (Sysno::fork, Process),
160 | (Sysno::vfork, Process),
161 | (Sysno::clone3, Process),
162 | (Sysno::clone, Process),
163 | (Sysno::execve, Process),
164 | (Sysno::getrusage, System),
165 | (Sysno::sysinfo, System),
166 | (Sysno::ptrace, System),
167 | (Sysno::uname, System),
168 | (Sysno::futex, System),
169 | (Sysno::set_robust_list, System),
170 | (Sysno::get_robust_list, System),
171 | (Sysno::eventfd, System),
172 | (Sysno::eventfd2, System),
173 | (Sysno::nanosleep, System),
174 | (Sysno::landlock_create_ruleset, Security),
175 | (Sysno::landlock_add_rule, Security),
176 | (Sysno::landlock_restrict_self, Security),
177 | (Sysno::getpriority, Process),
178 | (Sysno::setpriority, Process),
179 | ];
180 | array.into_iter().collect()
181 | }
182 |
--------------------------------------------------------------------------------
/src/syscall_object.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused_variables)]
2 | use crate::auxiliary::kernel_errno::{self};
3 | use crate::cli::{SUMMARY_ONLY, SYSCALLS_TO_TRACE};
4 | use crate::{types::Bytes, utilities::SYSKELETON_MAP};
5 | use nix::unistd::Pid;
6 | use std::{
7 | fmt::Display,
8 | mem::{self},
9 | };
10 |
11 | #[derive(Clone, Debug, PartialEq)]
12 | pub enum SyscallState {
13 | Entering,
14 | Exiting,
15 | }
16 |
17 | #[derive(Debug)]
18 | pub enum ErrnoVariant {
19 | Userland(nix::errno::Errno),
20 | Kernel(kernel_errno::KernelErrno),
21 | }
22 | impl Display for ErrnoVariant {
23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 | match self {
25 | ErrnoVariant::Userland(userland_errno) => {
26 | write!(f, "{:?}: {}", userland_errno, userland_errno.desc())
27 | }
28 | ErrnoVariant::Kernel(kernel_errno) => {
29 | write!(f, "{:?}: {}", kernel_errno, kernel_errno.desc())
30 | }
31 | }
32 | }
33 | }
34 |
35 | impl ErrnoVariant {
36 | pub fn desc(&self) -> &'static str {
37 | match self {
38 | ErrnoVariant::Userland(errno) => errno.desc(),
39 | ErrnoVariant::Kernel(kernel_errno) => kernel_errno.desc(),
40 | }
41 | }
42 | }
43 |
44 | #[derive(Debug)]
45 | pub enum SyscallResult {
46 | Fail(ErrnoVariant),
47 | Success(u64),
48 | }
49 |
50 | use syscalls::Sysno;
51 |
52 | #[derive(Debug)]
53 | pub struct SyscallObject {
54 | pub sysno: Sysno,
55 | pub tracee_pid: Pid,
56 | pub state: SyscallState,
57 | pub paused: bool,
58 | pub result: SyscallResult,
59 | pub currently_blocking: bool,
60 | }
61 |
62 | impl Default for SyscallObject {
63 | fn default() -> Self {
64 | SyscallObject {
65 | sysno: unsafe { mem::zeroed() },
66 | result: unsafe { mem::zeroed() },
67 | tracee_pid: unsafe { mem::zeroed() },
68 | state: SyscallState::Entering,
69 | paused: false,
70 | currently_blocking: false,
71 | }
72 | }
73 | }
74 |
75 | impl SyscallObject {
76 | pub fn fill_buffer(&mut self) {
77 | if let Ok(_) = self.one_line_formatter() {
78 | // let mut string = String::new();
79 | // for string_portion in &mut self.one_line {
80 | // string.push_str(&format!("{}", string_portion));
81 | // }
82 | // print!("{}", string)
83 | } else {
84 | // disabled for now
85 | // switch to syscallobject_annotation formatting
86 | // let mut annot_variant = SyscallObject_Annotations::from(self);
87 | // annot_variant.format()
88 | }
89 | }
90 | }
91 |
92 | impl SyscallObject {
93 | pub(crate) fn get_sysno(orig_rax: i32) -> Sysno {
94 | Sysno::from(orig_rax)
95 | }
96 |
97 | pub(crate) fn get_errno(&self) -> &ErrnoVariant {
98 | if let SyscallResult::Fail(ref errno_variant) = self.result {
99 | return errno_variant;
100 | }
101 | unreachable!()
102 | }
103 |
104 | pub(crate) fn build(tracee_pid: Pid, sysno: Sysno) -> Option {
105 | let _ = SYSKELETON_MAP.get(&sysno)?;
106 | Some(SyscallObject {
107 | sysno,
108 | tracee_pid,
109 | ..Default::default()
110 | })
111 | }
112 |
113 | pub(crate) fn should_skip_building(sysno: Sysno) -> bool {
114 | !SYSCALLS_TO_TRACE.contains(sysno) || *SUMMARY_ONLY
115 | }
116 |
117 | fn style_bytes(register_value: u64) -> String {
118 | let bytes_amount = register_value as usize;
119 | let mut bytes = Bytes::norm(bytes_amount);
120 | if bytes_amount as f64 / 1_000_000_000.0 > 1.0 {
121 | bytes = Bytes::giga(bytes_amount as f64 / 1_000_000_000.0)
122 | } else if bytes_amount as f64 / 1_000_000.0 > 1.0 {
123 | bytes = Bytes::mega(bytes_amount as f64 / 1_000_000.0)
124 | } else if bytes_amount as f64 / 1_000.0 > 1.0 {
125 | bytes = Bytes::kilo(bytes_amount as f64 / 1_000.0)
126 | }
127 | bytes.to_string()
128 | }
129 |
130 | pub(crate) fn style_bytes_length_specific(register_value: u64) -> String {
131 | let bytes_amount = register_value as usize;
132 | let mut bytes = Bytes::norm(bytes_amount);
133 | if bytes_amount / 1_000_000_000 > 1 {
134 | bytes = Bytes::giga(bytes_amount as f64 / 1_000_000_000.0)
135 | } else if bytes_amount / 1_000_000 > 1 {
136 | bytes = Bytes::mega(bytes_amount as f64 / 1_000_000.0)
137 | } else if bytes_amount / 1_000 > 1 {
138 | bytes = Bytes::kilo(bytes_amount as f64 / 1_000.0)
139 | }
140 | bytes.to_string()
141 | }
142 |
143 | pub(crate) fn is_mem_alloc_dealloc(&self) -> bool {
144 | // TODO!
145 | // scrutinize
146 | self.sysno == Sysno::brk || self.sysno == Sysno::mmap
147 | }
148 |
149 | pub(crate) fn is_exiting(&self) -> bool {
150 | self.sysno == Sysno::exit || self.sysno == Sysno::exit_group
151 | }
152 |
153 | pub(crate) fn has_errored(&self) -> bool {
154 | matches!(self.result, SyscallResult::Fail(_))
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/types.rs:
--------------------------------------------------------------------------------
1 |
2 | use crate::{colors::PAGES_COLOR, utilities::PAGE_SIZE};
3 | use colored::Colorize;
4 | use std::{fmt::Display, mem::MaybeUninit};
5 |
6 | pub type Annotation = [&'static str; 2];
7 |
8 | pub type SysAnnotations = (&'static str, &'static [Annotation], Annotation);
9 |
10 | #[derive(Clone)]
11 | pub struct Syscall_Shape {
12 | // pub types: &'static [SysArg],
13 | pub syscall_return: SysReturn,
14 | }
15 |
16 | type FD = &'static str;
17 | type PID = &'static str;
18 | type FD_PAIR = [&'static str; 2];
19 | type ARR = &'static [&'static str];
20 | type Errored = MaybeUninit;
21 | type ADDRESS = &'static str;
22 | type SIGNAL = &'static str;
23 | type TEXT = &'static str;
24 |
25 | #[derive(Clone, Copy, Debug)]
26 | pub enum SysReturn {
27 | Numeric_Or_Errno,
28 | Always_Successful_Numeric,
29 | Length_Of_Bytes_Specific_Or_Errno,
30 | Address_Or_Errno(ADDRESS),
31 | Address_Or_MAP_FAILED_Errno(ADDRESS),
32 | Address_Or_Errno_getcwd(ADDRESS),
33 | Signal_Or_Errno(SIGNAL),
34 | Priority_Or_Errno(Errored),
35 | File_Descriptor_Or_Errno(FD),
36 | Does_Not_Return_Anything,
37 | Ptrace_Diverse_Or_Errno,
38 | Always_Successful_User_Group,
39 | Always_Succeeds,
40 | Always_Errors,
41 | Never_Returns,
42 | }
43 |
44 | #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
45 | pub enum Category {
46 | Process,
47 | System,
48 | Thread,
49 | Memory,
50 | DiskIO,
51 | FileOp,
52 | Network,
53 | CPU,
54 | Security,
55 | Device,
56 | AsyncIO,
57 | Signals,
58 | }
59 |
60 | impl Display for Category {
61 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 | match self {
63 | Category::CPU => {
64 | write!(f, "CPU",)
65 | }
66 | Category::Memory => {
67 | write!(f, "Memory",)
68 | }
69 | Category::DiskIO => {
70 | write!(f, "Disk",)
71 | }
72 | Category::FileOp => {
73 | write!(f, "Disk",)
74 | }
75 | Category::Device => {
76 | write!(f, "Device",)
77 | }
78 | Category::Process => {
79 | write!(f, "Process",)
80 | }
81 | Category::AsyncIO => {
82 | write!(f, "AsyncIO",)
83 | }
84 | Category::Signals => {
85 | write!(f, "Signals",)
86 | }
87 | Category::Network => {
88 | write!(f, "Network",)
89 | }
90 | Category::Thread => {
91 | write!(f, "Thread",)
92 | }
93 | Category::System => {
94 | write!(f, "System",)
95 | }
96 | Category::Security => {
97 | write!(f, "Security",)
98 | }
99 | }
100 | }
101 | }
102 |
103 | // TODO!
104 | // consider humansize crate
105 |
106 | pub enum Bytes {
107 | norm(usize),
108 | kilo(f64),
109 | mega(f64),
110 | giga(f64),
111 | }
112 |
113 | impl Display for Bytes {
114 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 | match self {
116 | Bytes::norm(bytes) => {
117 | write!(f, "{:.1} Bytes", bytes)
118 | }
119 | Bytes::kilo(bytes) => {
120 | write!(f, "{:.1} Kib", bytes)
121 | }
122 | Bytes::mega(bytes) => {
123 | write!(f, "{:.1} Mib", bytes)
124 | }
125 | Bytes::giga(bytes) => {
126 | write!(f, "{:.1} Gib", bytes)
127 | }
128 | }
129 | }
130 | }
131 |
132 | impl From for Bytes {
133 | fn from(value: usize) -> Self {
134 | let value_float = value as f64;
135 | if (value_float / 1_073_741_824.0) >= 1.0 {
136 | Bytes::giga(value_float / 1_073_741_824.0)
137 | } else if (value_float / 1_048_576.0) >= 1.0 {
138 | Bytes::mega(value_float / 1_048_576.0)
139 | } else if (value_float / 1_024.0) >= 1.0 {
140 | Bytes::kilo(value_float / 1_024.0)
141 | } else {
142 | Bytes::norm(value)
143 | }
144 | }
145 | }
146 |
147 | pub enum BytesPagesRelevant {
148 | PagesCeil(Bytes),
149 | PagesFloor(Bytes),
150 | }
151 |
152 | impl BytesPagesRelevant {
153 | pub fn from_ceil(value: usize) -> Self {
154 | BytesPagesRelevant::PagesCeil(Bytes::from(value))
155 | }
156 | pub fn from_floor(value: usize) -> Self {
157 | BytesPagesRelevant::PagesFloor(Bytes::from(value))
158 | }
159 | }
160 |
161 | impl Display for BytesPagesRelevant {
162 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 | use Bytes::*;
164 | use BytesPagesRelevant::*;
165 | match self {
166 | PagesCeil(bytes) => match *bytes {
167 | norm(bytes) => {
168 | let pages = format!("{} Pages", f64::ceil(bytes as f64 / *PAGE_SIZE as f64))
169 | .custom_color(*PAGES_COLOR);
170 | write!(f, "{:.1} Bytes ({})", bytes, pages)
171 | }
172 | kilo(bytes) => {
173 | let pages =
174 | format!("{} Pages", f64::ceil((bytes * 1024.0) / *PAGE_SIZE as f64))
175 | .custom_color(*PAGES_COLOR);
176 | write!(f, "{:.1} KiB ({})", bytes, pages)
177 | }
178 | mega(bytes) => {
179 | let pages = format!(
180 | "{} Pages",
181 | f64::ceil((bytes * 1_048_576.0) / *PAGE_SIZE as f64)
182 | )
183 | .custom_color(*PAGES_COLOR);
184 | write!(f, "{:.1} MiB ({})", bytes, pages)
185 | }
186 | giga(bytes) => {
187 | let pages = format!(
188 | "{} Pages",
189 | f64::ceil((bytes * 1_073_741_824.0) / *PAGE_SIZE as f64)
190 | )
191 | .custom_color(*PAGES_COLOR);
192 | write!(f, "{:.1} GiB ({})", bytes, pages)
193 | }
194 | },
195 | PagesFloor(bytes) => match *bytes {
196 | norm(bytes) => {
197 | let pages = format!("{} Pages", f64::floor(bytes as f64 / *PAGE_SIZE as f64))
198 | .custom_color(*PAGES_COLOR);
199 |
200 | write!(f, "{:.1} Bytes ({})", bytes, pages)
201 | }
202 | kilo(bytes) => {
203 | let pages =
204 | format!("{} Pages", f64::floor((bytes * 1024.0) / *PAGE_SIZE as f64))
205 | .custom_color(*PAGES_COLOR);
206 |
207 | write!(f, "{:.1} KiB ({})", bytes, pages)
208 | }
209 | mega(bytes) => {
210 | let pages = format!(
211 | "{} Pages",
212 | f64::floor((bytes * 1_048_576.0) / *PAGE_SIZE as f64)
213 | )
214 | .custom_color(*PAGES_COLOR);
215 |
216 | write!(f, "{:.1} MiB ({})", bytes, pages)
217 | }
218 | giga(bytes) => {
219 | let pages = format!(
220 | "{} Pages",
221 | f64::floor((bytes * 1_073_741_824.0) / *PAGE_SIZE as f64)
222 | )
223 | .custom_color(*PAGES_COLOR);
224 |
225 | write!(f, "{:.1} GiB ({})", bytes, pages)
226 | }
227 | },
228 | }
229 | }
230 | }
231 |
232 | #[repr(C)]
233 | #[derive(Debug)]
234 | pub enum mlock2 {
235 | MLOCK_ONFAULT = 1,
236 | }
237 |
238 | #[repr(C)]
239 | #[derive(Debug)]
240 | pub enum LandlockCreateFlags {
241 | LANDLOCK_CREATE_RULESET_VERSION = 1,
242 | }
243 |
244 | #[repr(C)]
245 | #[derive(Debug)]
246 | pub enum LandlockRuleTypeFlags {
247 | LANDLOCK_RULE_PATH_BENEATH = 1,
248 | }
249 |
--------------------------------------------------------------------------------
/src/utilities.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | auxiliary::{constants::general::MAX_KERNEL_ULONG, kernel_errno::KernelErrno},
3 | colors::{switch_pathlike_color, PARTITION_1_COLOR, PARTITION_2_COLOR, PATHLIKE_ALTERNATOR},
4 | peeker_poker::{read_bytes_until_null, read_words_until_null},
5 | syscall_categories::initialize_categories_map,
6 | syscall_object::{ErrnoVariant, SyscallResult},
7 | syscall_skeleton_map::initialize_skeletons_map,
8 | types::{Bytes, BytesPagesRelevant, Category, Syscall_Shape},
9 | };
10 |
11 | use colored::{ColoredString, Colorize, CustomColor};
12 | use core::sync::atomic::AtomicUsize;
13 | use if_chain::if_chain;
14 | use nix::{
15 | errno::Errno,
16 | libc::{sysconf, AT_FDCWD, _SC_PAGESIZE},
17 | sys::signal::Signal,
18 | unistd::Pid,
19 | };
20 | use procfs::process::{MemoryMap, Process};
21 | use std::{
22 | borrow::Cow,
23 | collections::HashMap,
24 | hash::{DefaultHasher, Hash, Hasher},
25 | sync::{
26 | atomic::{AtomicBool, Ordering},
27 | LazyLock, Mutex,
28 | },
29 | time::Duration,
30 | };
31 | use syscalls::Sysno;
32 | use unicode_segmentation::Graphemes;
33 | use uzers::{Groups, Users};
34 |
35 | pub static PAGE_SIZE: LazyLock = LazyLock::new(|| unsafe { sysconf(_SC_PAGESIZE) as usize });
36 | pub static PRE_CALL_PROGRAM_BREAK_POINT: AtomicUsize = AtomicUsize::new(0);
37 | pub static REGISTERS: Mutex<[u64; 6]> = Mutex::new([0; 6]);
38 | pub static HALT_TRACING: AtomicBool = AtomicBool::new(false);
39 |
40 | pub static TABLE: LazyLock>> =
41 | LazyLock::new(|| Mutex::new(HashMap::new()));
42 | pub static TABLE_FOLLOW_FORKS: LazyLock>> =
43 | LazyLock::new(|| Mutex::new(HashMap::new()));
44 | // pub static SYSANNOT_MAP: LazyLock> =
45 | // LazyLock::new(|| initialize_annotations_map());
46 | pub static SYSKELETON_MAP: LazyLock> =
47 | LazyLock::new(initialize_skeletons_map);
48 | pub static SYSCATEGORIES_MAP: LazyLock> =
49 | LazyLock::new(initialize_categories_map);
50 | pub static FUTEXES: LazyLock>> =
51 | LazyLock::new(|| Mutex::new(HashMap::new()));
52 | pub static UZERS_CACHE: LazyLock> =
53 | LazyLock::new(|| Mutex::new(uzers::UsersCache::new()));
54 | pub static TRACEES: LazyLock>> =
55 | LazyLock::new(|| Mutex::new(HashMap::new()));
56 | // TODO!
57 | // switch to a string-interner implementation that remembers the last 5 pathlikes
58 | pub static LAST_PATHLIKE: LazyLock> = LazyLock::new(|| Mutex::new(0));
59 |
60 | pub fn lose_relativity_on_path(string: &str) -> &str {
61 | match string
62 | .chars()
63 | .enumerate()
64 | .skip_while(|&(_index, chara)| chara == '.' || chara == '/')
65 | .take(1)
66 | .next()
67 | {
68 | Some((index, _chara)) => &string[index..],
69 | None => "",
70 | }
71 | }
72 |
73 | pub fn get_mem_difference_from_previous(post_call_brk: usize) -> isize {
74 | post_call_brk as isize - PRE_CALL_PROGRAM_BREAK_POINT.load(Ordering::SeqCst) as isize
75 | }
76 |
77 | pub fn set_memory_break_pre_call(tracee_pid: Pid) {
78 | let mut tracees = TRACEES.lock().unwrap();
79 | let process = tracees
80 | .entry(tracee_pid)
81 | .or_insert_with(|| procfs::process::Process::new(i32::from(tracee_pid)).unwrap());
82 | let stat = process.stat().unwrap();
83 | let pre_call_brk = stat.start_brk.unwrap() as usize;
84 | PRE_CALL_PROGRAM_BREAK_POINT.store(pre_call_brk, Ordering::SeqCst);
85 | }
86 |
87 | pub fn where_in_tracee_memory(tracee_pid: Pid, address: u64) -> Option {
88 | let mut tracees = TRACEES.lock().unwrap();
89 | let process = tracees
90 | .entry(tracee_pid)
91 | .or_insert_with(|| procfs::process::Process::new(i32::from(tracee_pid)).unwrap());
92 | let maps = process.maps().ok()?.0;
93 | maps.into_iter()
94 | .find(|map| (address >= map.address.0) && (address <= map.address.1))
95 | }
96 |
97 | pub fn interpret_syscall_result(return_register: u64) -> SyscallResult {
98 | // TODO!
99 | // abandon the KernelErrno check and make it manual in restartable syscalls
100 | use ErrnoVariant::*;
101 | use SyscallResult::*;
102 |
103 | // strace does something similar to this
104 | // https://github.com/strace/strace/blob/0f9f46096fa8da84e2e6a6646cd1e326bf7e83c7/src/negated_errno.h#L17
105 | // https://github.com/strace/strace/blob/0f9f46096fa8da84e2e6a6646cd1e326bf7e83c7/src/linux/x86_64/get_error.c#L26
106 | if return_register > MAX_KERNEL_ULONG as u64 {
107 | let errno_positive = parse_as_long(return_register) * -1;
108 | let userland_errno = Errno::from_raw(errno_positive as i32);
109 | if matches!(userland_errno, Errno::UnknownErrno) {
110 | let kernel_errno = KernelErrno::from_i32(errno_positive as i32);
111 | if matches!(kernel_errno, KernelErrno::UnknownErrno) {
112 | // Large number but not an error
113 | return Success(return_register);
114 | }
115 | return Fail(Kernel(kernel_errno));
116 | }
117 | Fail(Userland(userland_errno))
118 | } else {
119 | Success(return_register)
120 | }
121 | }
122 |
123 | pub fn display_unsupported() {
124 | // unsafe {
125 | // UNSUPPORTED.iter().for_each(|uns| println!(" - {}", uns));
126 | // }
127 | }
128 |
129 | // this makes futexes more searchable
130 | pub fn calculate_futex_alias(mut futex_count: i32) -> String {
131 | let mut collector = String::new();
132 | while futex_count >= 0 {
133 | let remainder = futex_count % 26;
134 | let letter = (b'A' + remainder as u8) as char;
135 | collector.insert(0, letter);
136 | futex_count = (futex_count / 26) - 1;
137 | }
138 | collector.push_str(" ->");
139 | collector
140 | }
141 |
142 | pub fn parse_as_signal(signum: i32) -> &'static str {
143 | match Signal::try_from(signum) {
144 | Ok(signal) => signal.as_str(),
145 | Err(_e) => match signum {
146 | 32 => "SIGRT_32",
147 | 33 => "SIGRT_33",
148 | 34 => "SIGRT_34",
149 | 35 => "SIGRT_35",
150 | 36 => "SIGRT_36",
151 | 37 => "SIGRT_37",
152 | 38 => "SIGRT_38",
153 | 39 => "SIGRT_39",
154 | 40 => "SIGRT_40",
155 | 41 => "SIGRT_41",
156 | 42 => "SIGRT_42",
157 | 43 => "SIGRT_43",
158 | 44 => "SIGRT_44",
159 | 45 => "SIGRT_45",
160 | 46 => "SIGRT_46",
161 | 47 => "SIGRT_47",
162 | 48 => "SIGRT_48",
163 | 49 => "SIGRT_49",
164 | 50 => "SIGRT_50",
165 | 51 => "SIGRT_51",
166 | 52 => "SIGRT_52",
167 | 53 => "SIGRT_53",
168 | 54 => "SIGRT_54",
169 | 55 => "SIGRT_55",
170 | 56 => "SIGRT_56",
171 | 57 => "SIGRT_57",
172 | 58 => "SIGRT_58",
173 | 59 => "SIGRT_59",
174 | 60 => "SIGRT_60",
175 | 61 => "SIGRT_61",
176 | 62 => "SIGRT_62",
177 | 63 => "SIGRT_63",
178 | 64 => "SIGRT_64",
179 | _ => "[intentrace: signal not supported]",
180 | },
181 | }
182 | }
183 |
184 | pub fn parse_as_int(register: u64) -> i32 {
185 | unsafe { std::mem::transmute::(lower_32_bits(register)) }
186 | }
187 |
188 | pub fn parse_as_long(register: u64) -> i64 {
189 | unsafe { std::mem::transmute::(register) }
190 | }
191 |
192 | #[inline(always)]
193 | pub fn parse_as_ssize_t(register: usize) -> isize {
194 | unsafe { std::mem::transmute::(register) }
195 | }
196 |
197 | pub fn lower_32_bits(value: u64) -> u32 {
198 | (value & 0xFFFFFFFF) as u32
199 | }
200 |
201 | pub fn lower_64_bits(value: usize) -> u64 {
202 | (value & 0xFFFFFFFFFFFFFFFF) as u64
203 | }
204 |
205 | // Length_Of_Bytes_Specific
206 | // memory and file indexers and seekers where negative is expected
207 | pub fn parse_as_signed_bytes(register_value: u64) -> String {
208 | let bytes = unsafe { std::mem::transmute::(register_value) };
209 | // TODO!
210 | // phrasing should be checked for lseek and offsets in mmap
211 | format!("{bytes} Bytes")
212 | }
213 |
214 | // Length_Of_Bytes_Specific
215 | // memory and file indexers and seekers where negative is expected
216 | pub fn parse_as_unsigned_bytes(register_value: u64) -> String {
217 | format!("{register_value} Bytes")
218 | }
219 |
220 | pub fn parse_as_bytes_no_pages_ceil(register_value: usize) -> String {
221 | let bytes_pages = Bytes::from(register_value);
222 | bytes_pages.to_string()
223 | }
224 |
225 | // usually a size_t in mem syscalls
226 | pub fn parse_as_bytes_pages_ceil(register_value: usize) -> String {
227 | let bytes_pages = BytesPagesRelevant::from_ceil(register_value);
228 | bytes_pages.to_string()
229 | }
230 |
231 | // usually a size_t in mem syscalls
232 | fn parse_as_bytes_pages_floor(register_value: usize) -> String {
233 | let bytes_pages = BytesPagesRelevant::from_floor(register_value);
234 | bytes_pages.to_string()
235 | }
236 |
237 | // Use process_vm_readv(2)
238 | pub fn string_from_pointer(address: usize, tracee_pid: Pid) -> String {
239 | // TODO!
240 | // multi-threaded execve fails here for some reason
241 | match read_bytes_until_null(address, tracee_pid) {
242 | Some(data) => String::from_utf8_lossy(&data).into_owned(),
243 | None => "".to_owned(),
244 | }
245 | }
246 |
247 | pub fn get_array_of_strings(address: usize, tracee_pid: Pid) -> Vec {
248 | // TODO!
249 | // execve fails this
250 | let array_of_char_pointers = read_words_until_null(address, tracee_pid).unwrap();
251 | let mut strings = vec![];
252 | for char_pointer in array_of_char_pointers {
253 | strings.push(string_from_pointer(char_pointer, tracee_pid));
254 | }
255 | strings
256 | }
257 | fn calculate_hash(t: &str) -> u64 {
258 | let mut s = DefaultHasher::new();
259 | t.hash(&mut s);
260 | s.finish()
261 | }
262 |
263 | pub fn get_final_dentry_color_consider_repetition(repetition_dependent: &str) -> CustomColor {
264 | let last_pathlike = *LAST_PATHLIKE.lock().unwrap();
265 | if last_pathlike == calculate_hash(repetition_dependent) {
266 | // dont switch
267 | let alternator = *PATHLIKE_ALTERNATOR.lock().unwrap();
268 | PARTITION_2_COLOR[alternator]
269 | } else {
270 | *LAST_PATHLIKE.lock().unwrap() = calculate_hash(repetition_dependent);
271 | // switch
272 | switch_pathlike_color();
273 | let alternator = *PATHLIKE_ALTERNATOR.lock().unwrap();
274 | PARTITION_2_COLOR[alternator]
275 | }
276 | }
277 |
278 | pub fn partition_by_final_dentry(graphemes: Graphemes) -> (String, String) {
279 | let mut graphemes_revved = graphemes.rev();
280 | let second_partition = graphemes_revved
281 | .by_ref()
282 | .take_while(|chara| *chara != "/")
283 | .collect::>();
284 | let mut first_partition = graphemes_revved.rev().collect::();
285 | first_partition.push('/');
286 | (
287 | first_partition,
288 | second_partition.into_iter().rev().collect::(),
289 | )
290 | }
291 | // TODO!
292 | // lose_relativity_on_path requires book keeping
293 | // for now just print the path
294 | //
295 | pub fn get_strings_from_dirfd_anchored_file<'previous_function>(
296 | dirfd: i32,
297 | filename: &'previous_function str,
298 | tracee_pid: Pid,
299 | ) -> anyhow::Result<(Cow<'static, str>, &'previous_function str)> {
300 | if !filename.starts_with('/') {
301 | let mut tracees = TRACEES.lock().unwrap();
302 | let tracee_process = tracees
303 | .entry(tracee_pid)
304 | .or_insert_with(|| procfs::process::Process::new(i32::from(tracee_pid)).unwrap());
305 |
306 | if dirfd == AT_FDCWD {
307 | let current_working_directory = tracee_process.cwd()?;
308 |
309 | // remove this check when lose_relativity_on_path starts accounting for path math
310 | let repetition_dependent = if filename.starts_with("./") {
311 | &filename[2..]
312 | } else {
313 | filename.as_ref()
314 | };
315 | // let repetition_dependent = lose_relativity_on_path(filename.as_ref());
316 | Ok((
317 | Cow::Owned(current_working_directory.to_string_lossy().into_owned()),
318 | repetition_dependent,
319 | ))
320 | } else {
321 | let file_info = tracee_process.fd_from_fd(dirfd)?;
322 | match file_info.target {
323 | procfs::process::FDTarget::Path(first_partition) => {
324 | // remove this check when lose_relativity_on_path starts accounting for path math
325 | let repetition_dependent = if filename.starts_with("./") {
326 | &filename[2..]
327 | } else {
328 | filename.as_ref()
329 | };
330 | // let repetition_dependent = lose_relativity_on_path(filename.as_ref());
331 | Ok((
332 | Cow::Owned(first_partition.to_string_lossy().into_owned()),
333 | repetition_dependent,
334 | ))
335 | }
336 | _ => unreachable!(),
337 | }
338 | }
339 | } else {
340 | Ok((Cow::Borrowed(""), filename))
341 | }
342 | }
343 |
344 | pub fn parse_as_file_descriptor(file_descriptor: i32, tracee_pid: Pid) -> String {
345 | use procfs::process::FDTarget;
346 | let mut colored_strings = Vec::new();
347 | if file_descriptor == 0 {
348 | return "0 -> StdIn".bright_blue().to_string();
349 | } else if file_descriptor == 1 {
350 | return "1 -> StdOut".bright_blue().to_string();
351 | } else if file_descriptor == 2 {
352 | return "2 -> StdErr".bright_blue().to_string();
353 | } else {
354 | let mut tracees = TRACEES.lock().unwrap();
355 | let process = tracees
356 | .entry(tracee_pid)
357 | .or_insert_with(|| procfs::process::Process::new(i32::from(tracee_pid)).unwrap());
358 | let file_info = process.fd_from_fd(file_descriptor);
359 | match file_info {
360 | Ok(file) => match file.target {
361 | FDTarget::Path(path) => {
362 | use unicode_segmentation::UnicodeSegmentation;
363 | colored_strings.push(format!("{} -> ", file.fd).bright_blue());
364 | let graphemes = path.to_str().unwrap().graphemes(true);
365 | let (yellow, repetition_dependent) = partition_by_final_dentry(graphemes);
366 | let partition_2_color =
367 | get_final_dentry_color_consider_repetition(&repetition_dependent);
368 | colored_strings.push(yellow.custom_color(*PARTITION_1_COLOR));
369 | colored_strings.push(repetition_dependent.custom_color(partition_2_color));
370 | }
371 | FDTarget::Socket(socket_number) => {
372 | use procfs::net;
373 | let mut tcp = net::tcp().unwrap_or(vec![]);
374 | tcp.extend(net::tcp6().unwrap_or(vec![]));
375 | 'lookup: {
376 | if let Some(matching_socket) =
377 | tcp.into_iter().find(|entry| entry.inode == socket_number)
378 | {
379 | match matching_socket.remote_address.ip().is_loopback() {
380 | true => colored_strings.push(
381 | format!(
382 | "{} -> localhost:{}",
383 | file.fd,
384 | matching_socket.remote_address.port()
385 | )
386 | .bright_blue(),
387 | ),
388 | false => colored_strings.push(
389 | format!(
390 | "{} -> {}:{}",
391 | file.fd,
392 | matching_socket.remote_address.ip().to_string(),
393 | matching_socket.remote_address.port()
394 | )
395 | .bright_blue(),
396 | ),
397 | };
398 | break 'lookup;
399 | }
400 | if_chain! {
401 | if let Ok(entries) = net::tcp6();
402 | if let Some(matching_socket) = entries.into_iter().find(|entry| entry.inode == socket_number);
403 | then {
404 | match matching_socket.remote_address.ip().is_loopback() {
405 | true => colored_strings.push(
406 | format!(
407 | "{} -> localhost:{}",
408 | file.fd,
409 | matching_socket.remote_address.port()
410 | )
411 | .bright_blue(),
412 | ),
413 | false => colored_strings.push(
414 | format!(
415 | "{} -> {}:{}",
416 | file.fd,
417 | matching_socket.remote_address.ip().to_string(),
418 | matching_socket.remote_address.port()
419 | )
420 | .bright_blue(),
421 | ),
422 | };
423 | break 'lookup;
424 | }
425 | }
426 | if_chain! {
427 | if let Ok(entries) = net::udp();
428 | if let Some(_) = entries.into_iter().find(|entry| entry.inode == socket_number);
429 | then {
430 | colored_strings
431 | .push(format!("{} -> UDP Socket", file.fd).bright_blue());
432 | break 'lookup;
433 | }
434 | }
435 | if_chain! {
436 | if let Ok(entries) = net::udp6();
437 | if let Some(_) = entries.into_iter().find(|entry| entry.inode == socket_number);
438 | then {
439 | colored_strings
440 | .push(format!("{} -> UDP Socket", file.fd).bright_blue());
441 | break 'lookup;
442 | }
443 | }
444 | if_chain! {
445 | if let Ok(entries) = net::unix();
446 | if let Some(_) = entries.into_iter().find(|entry| entry.inode == socket_number);
447 | then {
448 | colored_strings
449 | .push(format!("{} -> Unix Domain Socket", file.fd).bright_blue());
450 | break 'lookup;
451 | }
452 | }
453 | }
454 | }
455 | FDTarget::Net(_net) => {
456 | return format!("{} -> NET", file.fd).bright_blue().to_string()
457 | }
458 | FDTarget::Pipe(_pipe) => {
459 | return format!("{} -> Unix Pipe", file.fd)
460 | .bright_blue()
461 | .to_string()
462 | }
463 | FDTarget::AnonInode(anon_inode) => {
464 | // anon_inode is basically a file that has no inode on disk
465 | // anon_inode could've been something that was a file that is no longer on the disk
466 | // Some syscalls create file descriptors that have no inode
467 | // epoll_create, eventfd, inotify_init, signalfd, and timerfd
468 | // the entry will be a symbolic link with contents "anon_inode:"
469 | // An anon_inode shows that there's a file descriptor which has no referencing inode
470 |
471 | // open syscall can be used to create an anon inode
472 | // int fd = open( "/tmp/file", O_CREAT | O_RDWR, 0666 );
473 | // unlink( "/tmp/file" );
474 | return format!("{} -> {anon_inode}", file.fd)
475 | .bright_blue()
476 | .to_string();
477 | }
478 | FDTarget::MemFD(mem_fd) => {
479 | return format!("{} -> {mem_fd}", file.fd).bright_blue().to_string()
480 | }
481 | FDTarget::Other(target, _inode_number) => {
482 | return format!("{} -> {target}", file.fd).bright_blue().to_string()
483 | }
484 | },
485 | Err(_e) => return "ignored".to_owned(),
486 | }
487 | }
488 | String::from_iter(
489 | colored_strings
490 | .into_iter()
491 | .map(|colored_string| colored_string.to_string()),
492 | )
493 | }
494 |
495 | pub fn find_fd_for_tracee(file_descriptor: i32, tracee_pid: Pid) -> Option {
496 | let mut tracees = TRACEES.lock().unwrap();
497 | let process = tracees
498 | .entry(tracee_pid)
499 | .or_insert_with(|| procfs::process::Process::new(i32::from(tracee_pid)).unwrap());
500 | let mut fds = process.fd().ok()?;
501 | let descriptor_found = fds.find(|fd_iter| fd_iter.as_ref().unwrap().fd == file_descriptor)?;
502 | let descriptor_unwrapped = descriptor_found.ok()?;
503 | if let procfs::process::FDTarget::Path(path_buf) = descriptor_unwrapped.target {
504 | Some(path_buf.to_string_lossy().to_string())
505 | } else {
506 | None
507 | }
508 | }
509 |
510 | pub fn get_username_from_uid(owner: u32) -> Option<&'static str> {
511 | let cache = UZERS_CACHE.lock().unwrap();
512 | let user = cache.get_user_by_uid(owner)?;
513 | let name_str = user.name().to_str()?;
514 | Some(name_str.to_owned().leak())
515 | }
516 |
517 | pub fn get_groupname_from_uid(group: u32) -> Option<&'static str> {
518 | let cache = UZERS_CACHE.lock().unwrap();
519 | let group_retrieved = cache.get_group_by_gid(group)?;
520 | let group = group_retrieved.name().to_str()?;
521 | Some(group.to_owned().leak())
522 | }
523 |
524 | pub fn new_process() -> ColoredString {
525 | "
526 |
527 | ╭────────────────╮
528 | │ │
529 | │ NEW PROCESS │
530 | │ │
531 | ╰────────────────╯
532 | "
533 | .custom_color(colored::CustomColor {
534 | r: 223,
535 | g: 128,
536 | b: 8,
537 | })
538 | }
539 |
540 | pub fn new_thread() -> ColoredString {
541 | "
542 |
543 | ╭────────────────╮
544 | │ │
545 | │ NEW THREAD │
546 | │ │
547 | ╰────────────────╯
548 | "
549 | .green()
550 | }
551 |
552 | // TODO!
553 | // consider blinking arrows as replacement
554 | pub fn syscall_is_blocking() -> ColoredString {
555 | " ╭─────────────────────╮
556 | │ SYSCALL BLOCKED │
557 | ╰─────────────────────╯
558 | "
559 | .cyan()
560 | }
561 |
562 | // TODO!
563 | // check how strace does this, maybe its better
564 | pub fn colorize_syscall_name(sysno: &Sysno, category: &Category) -> ColoredString {
565 | match category {
566 | // green
567 | Category::Process => sysno.name().bold().green(),
568 | Category::Thread => sysno.name().bold().green(),
569 | Category::CPU => sysno.name().bold().green(),
570 |
571 | Category::Network => sysno.name().bold().green(),
572 |
573 | // ram
574 | Category::Memory => sysno.name().bold().bright_red(),
575 |
576 | // bluish
577 | Category::FileOp => sysno.name().bold().blue(),
578 | Category::DiskIO => sysno.name().bold().bright_blue(),
579 | Category::Security => sysno.name().bold().bright_cyan(),
580 |
581 | // black
582 | Category::System => sysno.name().bold().cyan(),
583 |
584 | // exotic
585 | Category::Signals => sysno.name().bold().bright_purple(),
586 | Category::Device => sysno.name().bold().bright_yellow(),
587 | Category::AsyncIO => sysno.name().bold().purple(),
588 | }
589 | }
590 |
--------------------------------------------------------------------------------
/src/writer.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | cli::OUTPUT_FILE,
3 | colors::{
4 | EXITED_BACKGROUND_COLOR, GENERAL_TEXT_COLOR, OUR_YELLOW, PAGES_COLOR, PARTITION_1_COLOR,
5 | PARTITION_2_COLOR, PATHLIKE_ALTERNATOR, PID_BACKGROUND_COLOR,
6 | },
7 | utilities::{
8 | calculate_futex_alias, get_final_dentry_color_consider_repetition,
9 | get_strings_from_dirfd_anchored_file, partition_by_final_dentry, FUTEXES, PAGE_SIZE,
10 | },
11 | };
12 | use colored::{ColoredString, Colorize, CustomColor};
13 | use nix::unistd::Pid;
14 | use num_traits::{PrimInt, Signed, Unsigned};
15 | use std::{
16 | borrow::Cow,
17 | io::{BufWriter, Write},
18 | sync::{LazyLock, Mutex, OnceLock},
19 | };
20 | use syscalls::Sysno;
21 |
22 | pub static BUFFER: LazyLock>> = LazyLock::new(|| Mutex::new(Vec::new()));
23 | pub static WRITER: OnceLock>>> = OnceLock::new();
24 |
25 | pub fn initialize_writer() {
26 | // colored crate disables stderr's coloring when stdout is redirected elsewhere, e.g.: /dev/null
27 | // this is a workaround for now
28 | // https://github.com/colored-rs/colored/issues/125#issuecomment-1691155922
29 | use colored;
30 | colored::control::set_override(true);
31 |
32 | let sink: Box = if let Some(output) = *OUTPUT_FILE {
33 | match std::fs::File::options()
34 | .append(false)
35 | .write(true)
36 | .create(true)
37 | .truncate(true)
38 | .open(output)
39 | {
40 | Ok(file) => Box::new(file),
41 | Err(_) => {
42 | eprintln!("Could not open or create file: {}", output.display());
43 | std::process::exit(100);
44 | }
45 | }
46 | } else {
47 | Box::new(std::io::stderr())
48 | };
49 | let _ = WRITER.set(Mutex::new(BufWriter::new(sink)));
50 | }
51 |
52 | #[inline(always)]
53 | pub(crate) fn write_general_text(arg: &str) {
54 | buffered_write(arg.custom_color(*GENERAL_TEXT_COLOR));
55 | }
56 |
57 | #[inline(always)]
58 | pub(crate) fn write_text(text: ColoredString) {
59 | buffered_write(text);
60 | }
61 |
62 | #[inline(always)]
63 | pub fn buffered_write(data: ColoredString) {
64 | BUFFER.lock().unwrap().push(data);
65 | }
66 |
67 | #[inline(always)]
68 | pub(crate) fn errorize_pid_color(text: ColoredString) {
69 | BUFFER.lock().unwrap()[0] = text;
70 | }
71 |
72 | #[inline(always)]
73 | pub fn empty_buffer() {
74 | let mut buffer = BUFFER.lock().unwrap();
75 | buffer.clear();
76 | }
77 |
78 | #[inline(always)]
79 | pub fn flush_buffer() {
80 | use std::io::Write;
81 | let mut buffer = BUFFER.lock().unwrap();
82 | let mut writer = WRITER.get().unwrap().lock().unwrap();
83 | for colored_text in buffer.iter_mut() {
84 | write!(writer, "{}", colored_text).unwrap();
85 | }
86 | writer.flush().unwrap();
87 | }
88 |
89 | #[inline(always)]
90 | pub fn write_parenthesis(string: &str) {
91 | write_general_text(" (");
92 | write_text(string.custom_color(*OUR_YELLOW));
93 | write_general_text(")");
94 | }
95 |
96 | #[inline(always)]
97 | pub fn write_syscall_not_covered(sysno: Sysno, tracee_pid: Pid) {
98 | buffered_write(tracee_pid.to_string().white());
99 | buffered_write(" ".dimmed());
100 | buffered_write(sysno.name().white());
101 | buffered_write(" - ".dimmed());
102 | buffered_write("[intentrace: syscall not covered yet]".white());
103 | buffered_write("\n".dimmed());
104 | flush_buffer();
105 | }
106 |
107 | pub fn write_vanilla_path_file(filename: &str) {
108 | use unicode_segmentation::UnicodeSegmentation;
109 | let graphemes = filename.graphemes(true);
110 | let (yellow, repetition_dependent) = partition_by_final_dentry(graphemes);
111 |
112 | write_path_consider_repetition(&yellow, &repetition_dependent);
113 | }
114 |
115 | pub fn write_colored(filename: String) {
116 | buffered_write(filename.normal())
117 | }
118 |
119 | pub fn write_path_consider_repetition(yellow: &str, repetition_dependent: &str) {
120 | write_partition_1_consider_repetition(yellow);
121 | write_partition_2_consider_repetition(repetition_dependent);
122 | }
123 |
124 | // repetition isnt relevant for the first partition of the path
125 | // but the name is kept for consistency
126 | #[inline(always)]
127 | pub fn write_partition_1_consider_repetition(partition1: &str) {
128 | buffered_write(partition1.custom_color(*PARTITION_1_COLOR));
129 | }
130 |
131 | #[inline(always)]
132 | pub fn write_partition_2_consider_repetition(partition2: &str) {
133 | // don't alternate if we're operating on a directory
134 | let partition_2_color = if partition2.len() != 0 {
135 | get_final_dentry_color_consider_repetition(partition2)
136 | } else {
137 | PARTITION_2_COLOR[*PATHLIKE_ALTERNATOR.lock().unwrap()]
138 | };
139 | buffered_write(partition2.custom_color(partition_2_color));
140 | }
141 |
142 | pub fn write_possible_dirfd_anchor(
143 | dirfd: i32,
144 | filename: String,
145 | tracee_pid: Pid,
146 | ) -> anyhow::Result<()> {
147 | let (first_partition, second_partition) =
148 | get_strings_from_dirfd_anchored_file(dirfd, filename.as_ref(), tracee_pid)?;
149 | match first_partition {
150 | Cow::Owned(string) => {
151 | write_path_consider_repetition(string.as_ref(), second_partition);
152 | }
153 | Cow::Borrowed(_empty) => {
154 | write_vanilla_path_file(second_partition);
155 | }
156 | }
157 | Ok(())
158 | }
159 |
160 | pub fn write_directives(mut vector: Vec) {
161 | if !vector.is_empty() {
162 | // first element
163 | write_general_text(" (");
164 | write_text(vector.pop().unwrap());
165 | // remaining elements
166 | for entry in vector {
167 | write_general_text(", ");
168 | write_text(entry);
169 | }
170 | write_general_text(")");
171 | }
172 | }
173 |
174 | pub fn write_commas(mut vector: Vec) {
175 | if !vector.is_empty() {
176 | // first element
177 | write_text(vector.pop().unwrap());
178 | // remaining elements
179 | for entry in vector {
180 | write_general_text(", ");
181 | write_text(entry);
182 | }
183 | }
184 | }
185 |
186 | pub fn write_oring(mut vector: Vec) {
187 | if !vector.is_empty() {
188 | // first element
189 | write_text(vector.pop().unwrap());
190 | // remaining elements
191 | for entry in vector {
192 | write_general_text(", or ");
193 | write_text(entry);
194 | }
195 | }
196 | }
197 |
198 | pub fn write_anding(vector: Vec) {
199 | let mut vector_iter = vector.into_iter();
200 | // first element
201 | if let Some(entry) = vector_iter.next() {
202 | write_text(entry);
203 | }
204 | // second and remaining elements
205 | if let Some(second_as_last) = vector_iter.next() {
206 | for entry in vector_iter {
207 | write_general_text(", ");
208 | write_text(entry);
209 | }
210 | // last element
211 | write_general_text(", and ");
212 | write_text(second_as_last);
213 | }
214 | }
215 | use thousands::Separable;
216 | pub fn write_timespec(seconds: i64, nanoseconds: i64) {
217 | if seconds == 0 {
218 | if nanoseconds == 0 {
219 | write_text("immediately".custom_color(*OUR_YELLOW));
220 | } else {
221 | write_text("after ".custom_color(*OUR_YELLOW));
222 | write_text(
223 | nanoseconds
224 | .separate_with_commas()
225 | .custom_color(*PAGES_COLOR),
226 | );
227 | write_text(" nanoseconds".custom_color(*OUR_YELLOW));
228 | }
229 | } else {
230 | write_text("after ".custom_color(*OUR_YELLOW));
231 | write_text(seconds.separate_with_commas().custom_color(*PAGES_COLOR));
232 | write_text(" seconds".custom_color(*OUR_YELLOW));
233 | if nanoseconds != 0 {
234 | write_general_text(", ");
235 | write_text(
236 | nanoseconds
237 | .separate_with_commas()
238 | .custom_color(*PAGES_COLOR),
239 | );
240 | write_text(" nanoseconds".custom_color(*OUR_YELLOW));
241 | }
242 | }
243 | }
244 |
245 | pub fn write_timespec_non_relative(seconds: i64, nanoseconds: i64) {
246 | if seconds == 0 {
247 | if nanoseconds == 0 {
248 | write_text("0".custom_color(*PAGES_COLOR));
249 | write_text(" nano-seconds".custom_color(*OUR_YELLOW));
250 | } else {
251 | write_text(
252 | nanoseconds
253 | .separate_with_commas()
254 | .custom_color(*PAGES_COLOR),
255 | );
256 | write_text(" nano-seconds".custom_color(*OUR_YELLOW));
257 | }
258 | } else {
259 | write_text(seconds.separate_with_commas().custom_color(*PAGES_COLOR));
260 | write_text(" seconds".custom_color(*OUR_YELLOW));
261 | if nanoseconds != 0 {
262 | write_general_text(" and ");
263 | write_text(
264 | nanoseconds
265 | .separate_with_commas()
266 | .custom_color(*PAGES_COLOR),
267 | );
268 | write_text(" nanoseconds".custom_color(*OUR_YELLOW));
269 | }
270 | }
271 | }
272 |
273 | pub fn write_timeval(seconds: i64, microseconds: i64) {
274 | if seconds == 0 {
275 | if microseconds == 0 {
276 | write_text("immediately".custom_color(*OUR_YELLOW));
277 | } else {
278 | write_text("after ".custom_color(*OUR_YELLOW));
279 | write_text(
280 | microseconds
281 | .separate_with_commas()
282 | .custom_color(*PAGES_COLOR),
283 | );
284 | write_text(" microseconds".custom_color(*OUR_YELLOW));
285 | }
286 | } else {
287 | write_text("after ".custom_color(*OUR_YELLOW));
288 | write_text(seconds.separate_with_commas().custom_color(*PAGES_COLOR));
289 | write_text(" seconds".custom_color(*OUR_YELLOW));
290 | if microseconds != 0 {
291 | write_general_text(", ");
292 | write_text(
293 | microseconds
294 | .separate_with_commas()
295 | .custom_color(*PAGES_COLOR),
296 | );
297 | write_text(" microseconds".custom_color(*OUR_YELLOW));
298 | }
299 | }
300 | }
301 |
302 | // TODO!
303 | // perf
304 | pub fn write_signed_numeric(signed_numeric: impl PrimInt + Signed) {
305 | match signed_numeric.to_isize().unwrap() {
306 | -32767 => write_text("i16::MIN + 1".custom_color(CustomColor::new(0, 169, 233))),
307 | -32768 => write_text("i16::MIN".custom_color(CustomColor::new(0, 169, 233))),
308 | -32769 => write_text("i16::MIN - 1".custom_color(CustomColor::new(0, 169, 233))),
309 | -2147483647 => write_text("i32::MIN + 1".custom_color(CustomColor::new(0, 169, 233))),
310 | -2147483648 => write_text("i32::MIN".custom_color(CustomColor::new(0, 169, 233))),
311 | -2147483649 => write_text("i32::MIN - 1".custom_color(CustomColor::new(0, 169, 233))),
312 | -9223372036854775807 => {
313 | write_text("i64::MIN + 1".custom_color(CustomColor::new(0, 169, 233)))
314 | }
315 | -9223372036854775808 => write_text("i64::MIN".custom_color(CustomColor::new(0, 169, 233))),
316 | n => write_text(n.to_string().custom_color(CustomColor::new(0, 169, 233))),
317 | };
318 | }
319 |
320 | // TODO!
321 | // perf
322 | pub fn write_unsigned_numeric(unsigned_numeric: impl PrimInt + Unsigned) {
323 | match unsigned_numeric.to_usize().unwrap() {
324 | 32766 => write_text("i16::MAX - 1".custom_color(CustomColor::new(0, 169, 233))),
325 | 32767 => write_text("i16::MAX".custom_color(CustomColor::new(0, 169, 233))),
326 | 32768 => write_text("i16::MAX + 1".custom_color(CustomColor::new(0, 169, 233))),
327 | 65534 => write_text("u16::MAX - 1".custom_color(CustomColor::new(0, 169, 233))),
328 | 65535 => write_text("u16::MAX".custom_color(CustomColor::new(0, 169, 233))),
329 | 65536 => write_text("u16::MAX + 1".custom_color(CustomColor::new(0, 169, 233))),
330 | 2147483646 => write_text("i32::MAX - 1".custom_color(CustomColor::new(0, 169, 233))),
331 | 2147483647 => write_text("i32::MAX".custom_color(CustomColor::new(0, 169, 233))),
332 | 2147483648 => write_text("i32::MAX + 1".custom_color(CustomColor::new(0, 169, 233))),
333 | 4294967294 => write_text("u32::MAX - 1".custom_color(CustomColor::new(0, 169, 233))),
334 | 4294967295 => write_text("u32::MAX".custom_color(CustomColor::new(0, 169, 233))),
335 | 4294967296 => write_text("u32::MAX + 1".custom_color(CustomColor::new(0, 169, 233))),
336 | 9223372036854775806 => {
337 | write_text("i64::MAX - 1".custom_color(CustomColor::new(0, 169, 233)))
338 | }
339 | 9223372036854775807 => write_text("i64::MAX".custom_color(CustomColor::new(0, 169, 233))),
340 | 9223372036854775808 => {
341 | write_text("i64::MAX + 1".custom_color(CustomColor::new(0, 169, 233)))
342 | }
343 | 18446744073709551614 => {
344 | write_text("u64::MAX - 1".custom_color(CustomColor::new(0, 169, 233)))
345 | }
346 | 18446744073709551615 => write_text("u64::MAX".custom_color(CustomColor::new(0, 169, 233))),
347 | n => write_text(n.to_string().custom_color(CustomColor::new(0, 169, 233))),
348 | }
349 | }
350 |
351 | // CONVERSION OUTSIDE
352 | pub fn write_address(register_value: usize) {
353 | let pointer = register_value as *const ();
354 | if pointer.is_null() {
355 | write_text("NULL".custom_color(*OUR_YELLOW))
356 | } else {
357 | write_text(format!("{pointer:p}").custom_color(*OUR_YELLOW))
358 | }
359 | }
360 |
361 | // CONVERSION OUTSIDE
362 | pub fn write_page_aligned_address(register_value: usize) {
363 | let pointer = register_value as *const ();
364 | if pointer.is_null() {
365 | write_text("NULL".custom_color(*OUR_YELLOW))
366 | } else {
367 | // the pipe notation can only be used for 4KiB & 64KiB page sizes
368 | // 16KiB is a common page size but its equivalent to 250 in decimal
369 | let pointer_formatted = format!("{pointer:p}");
370 | match *PAGE_SIZE {
371 | 4096 => {
372 | let len = pointer_formatted.len();
373 | write_text(pointer_formatted[..(len - 3)].custom_color(*OUR_YELLOW));
374 | if register_value % 4096 == 0 {
375 | write_text("|".custom_color(*PAGES_COLOR));
376 | } else {
377 | write_text("|".red());
378 | }
379 | write_text(pointer_formatted[(len - 3)..].custom_color(*OUR_YELLOW));
380 | }
381 | 65536 => {
382 | let len = pointer_formatted.len();
383 | write_text(pointer_formatted[..(len - 4)].custom_color(*OUR_YELLOW));
384 | if register_value % 65536 == 0 {
385 | write_text("|".custom_color(*PAGES_COLOR));
386 | } else {
387 | write_text("|".red());
388 | }
389 | write_text(pointer_formatted[(len - 4)..].custom_color(*OUR_YELLOW));
390 | }
391 | 16384 => {
392 | let len = pointer_formatted.len();
393 | write_text(pointer_formatted[..(len - 5)].custom_color(*OUR_YELLOW));
394 | if register_value % 16384 == 0 {
395 | write_text(
396 | pointer_formatted[(len - 5)..(len - 4)]
397 | .custom_color(*PAGES_COLOR)
398 | .underline(),
399 | );
400 | } else {
401 | write_text(pointer_formatted[(len - 5)..(len - 4)].red().underline());
402 | }
403 | write_text(pointer_formatted[(len - 4)..].custom_color(*OUR_YELLOW));
404 | }
405 | _ => write_text(pointer_formatted.custom_color(*OUR_YELLOW)),
406 | }
407 | }
408 | }
409 |
410 | pub fn write_futex(futex_address: usize) {
411 | let mut futexes = FUTEXES.lock().unwrap();
412 | let number_of_futexes = futexes.len();
413 | let futex_alias = futexes.entry(futex_address).or_insert_with(|| {
414 | calculate_futex_alias(number_of_futexes as _).custom_color(*PAGES_COLOR)
415 | });
416 | write_text(futex_alias.clone());
417 | write_text(format!(" {:p}", futex_address as *const ()).custom_color(*OUR_YELLOW));
418 | }
419 |
420 | pub fn write_exiting(process_pid: Pid) {
421 | let exited = " EXITED ".on_custom_color(*EXITED_BACKGROUND_COLOR);
422 | let pid = format!(" {} ", process_pid).on_custom_color(*PID_BACKGROUND_COLOR);
423 | write_text("\n\n ".white());
424 | write_text(pid);
425 | write_text(exited);
426 | write_text("\n\n".white());
427 | }
428 |
--------------------------------------------------------------------------------