├── other_things ├── echo_input ├── singlestep_vs_group_stop ├── profiling.txt ├── questions_to_debugger_people.txt ├── cpuid.c ├── debug_registers_experiment.c ├── hashmap_vs_sort_bench.rs ├── echo_input.c ├── poketext_experiment.cpp ├── load_symbols.rs ├── pretty.txt ├── interp_sketch.rs ├── singlestep_vs_group_stop.cpp ├── tui_sketch.rs ├── varint_benchmark.c ├── demangler_exploration.rs └── some_dwarf_stats.txt ├── testprogs ├── bad_function_call.c ├── types_across_units │ ├── a.o │ ├── b.o │ ├── main │ ├── main.o │ ├── main.c │ ├── a.c │ ├── b.c │ ├── build.sh │ └── p.h ├── simple_loop.cpp ├── vdso.c ├── dlopen │ ├── build.sh │ ├── dl.c │ └── main.c ├── self_reference.cpp ├── simple_inline.c ├── tiny.c ├── file_read.c ├── simple_recursion.c ├── structs_with_same_name.cpp ├── its_a_trap.c ├── absl │ ├── build.sh │ ├── CMakeLists.txt │ └── containers.cpp ├── exception.cpp ├── anon_types.cpp ├── string.cpp ├── many_threads.cpp ├── string_rs.rs ├── simple_threads.cpp ├── simple_signal_handler.c ├── simple_mutex.cpp ├── arrays.cpp ├── types.cpp ├── types_rs.rs ├── global_variables.cpp ├── function_and_member_pointers.cpp ├── signal_handler_with_threads.c ├── simple_codegen.c ├── panic.rs ├── simple_tls.cpp ├── build.sh ├── simple_coro.cpp ├── simple_async_rs.rs ├── containers_zig.zig ├── simple_simd.c ├── containers_rs.rs └── containers.cpp ├── .gitignore ├── .cargo └── config.toml ├── about.toml ├── src ├── context.rs ├── executor.rs ├── range_index.rs ├── pool.rs ├── error.rs ├── log.rs └── os.rs ├── about.hbs ├── README.md ├── Cargo.toml ├── release.sh ├── todo ├── LICENSE └── Cargo.lock /other_things/echo_input: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/other_things/echo_input -------------------------------------------------------------------------------- /testprogs/bad_function_call.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int (*f)(int) = (int (*)(int))42; 3 | f(10); 4 | } 5 | -------------------------------------------------------------------------------- /testprogs/types_across_units/a.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/testprogs/types_across_units/a.o -------------------------------------------------------------------------------- /testprogs/types_across_units/b.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/testprogs/types_across_units/b.o -------------------------------------------------------------------------------- /testprogs/types_across_units/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/testprogs/types_across_units/main -------------------------------------------------------------------------------- /testprogs/types_across_units/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/testprogs/types_across_units/main.o -------------------------------------------------------------------------------- /other_things/singlestep_vs_group_stop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/al13n321/nnd/HEAD/other_things/singlestep_vs_group_stop -------------------------------------------------------------------------------- /testprogs/simple_loop.cpp: -------------------------------------------------------------------------------- 1 | int main() { 2 | volatile long i = 0; 3 | while (true) { 4 | i = i + 1; 5 | i = i + 2; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /testprogs/vdso.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | struct timespec t; 6 | clock_gettime(0, &t); 7 | printf("%ld\n", t.tv_sec); 8 | } 9 | -------------------------------------------------------------------------------- /testprogs/dlopen/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BASEDIR=$(dirname "$0") 4 | gcc -g -O2 -shared -fPIC -o "$BASEDIR/build/dl.so" "$BASEDIR/dl.c" 5 | gcc -g -O2 -o "$BASEDIR/build/main" "$BASEDIR/main.c" 6 | -------------------------------------------------------------------------------- /other_things/profiling.txt: -------------------------------------------------------------------------------- 1 | `samply record` 2 | 3 | or: 4 | sudo perf record -F 1000 --call-graph=dwarf -- 5 | sudo chown al13n perf.data 6 | `hotspot` or profiler.firefox.com or `perf report` 7 | -------------------------------------------------------------------------------- /testprogs/dlopen/dl.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int __attribute__((noinline)) g(int x) { 4 | return x * 10; 5 | } 6 | 7 | extern void f(int x) { 8 | x = g(x); 9 | printf("%d\n", x); 10 | } 11 | -------------------------------------------------------------------------------- /testprogs/types_across_units/main.c: -------------------------------------------------------------------------------- 1 | #include "p.h" 2 | #include 3 | 4 | int main() { 5 | struct Pair p; 6 | fa(&p); 7 | fb(&p); 8 | printf("%lu %lu\n", (unsigned long)p.a, (unsigned long)p.b); 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /DWARF5.pdf 2 | /ignored_in_dropbox.txt 3 | /target 4 | /testprogs/build 5 | /testprogs/absl/build 6 | /testprogs/absl/lib 7 | /testprogs/dlopen/build 8 | /strategy_post_draft.txt 9 | /temp_post.txt 10 | /src/version.rs 11 | .#* 12 | -------------------------------------------------------------------------------- /testprogs/types_across_units/a.c: -------------------------------------------------------------------------------- 1 | #include "p.h" 2 | #include 3 | 4 | struct A { 5 | int a; 6 | }; 7 | 8 | struct A a; 9 | 10 | void fa(struct Pair *p) { 11 | a.a = 42; 12 | p->a = &a; 13 | printf("fa %d\n", p->a->a); 14 | } 15 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-C", "force-unwind-tables=yes", # backtrace on panic doen't work in debug mode without this (but works in release for some reason) 4 | "-C", "force-frame-pointers=yes", # make profilers work slightly better, maybe 5 | ] 6 | -------------------------------------------------------------------------------- /testprogs/self_reference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | struct A { 5 | A volatile & a; 6 | volatile int x = 42; 7 | 8 | A() : a(*this) {} 9 | }; 10 | 11 | int main() { 12 | A a; 13 | cout< 2 | 3 | inline void f(int n) { 4 | printf("%d", n*2); 5 | } 6 | 7 | inline void g(int n) { 8 | f(n*10); 9 | } 10 | 11 | void h(int n) { 12 | g(n+1); 13 | } 14 | 15 | int main() { 16 | g(1); 17 | } 18 | -------------------------------------------------------------------------------- /testprogs/tiny.c: -------------------------------------------------------------------------------- 1 | long __attribute__((noinline)) g(long i) { 2 | return i + 1; 3 | } 4 | 5 | void __attribute__((noinline)) f() { 6 | volatile long i = 0; 7 | while (i < 100000000000l) { 8 | i = g(i); 9 | } 10 | } 11 | 12 | int main() { 13 | f(); 14 | } 15 | -------------------------------------------------------------------------------- /testprogs/file_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | char buf[1024]; 7 | int fd = open("/proc/self/exe", O_RDONLY); 8 | read(fd, buf, 1024); 9 | printf("%d\n", buf[100]); 10 | close(fd); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /testprogs/simple_recursion.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int __attribute__((noinline)) f(int n) { 4 | volatile int x = 0; 5 | x = x + 1; 6 | if (n > 0) 7 | x = x + f(n-1); 8 | x = x + 1; 9 | return x; 10 | } 11 | 12 | int main() { 13 | printf("%d", f(5)); 14 | } 15 | -------------------------------------------------------------------------------- /testprogs/types_across_units/b.c: -------------------------------------------------------------------------------- 1 | #include "p.h" 2 | #include 3 | #include 4 | 5 | struct B { 6 | double b; 7 | }; 8 | 9 | void fb(struct Pair *p) { 10 | p->b = (struct B*)malloc(sizeof(struct B)); 11 | p->b->b = 1.23; 12 | printf("fb %f\n", p->b->b); 13 | } 14 | -------------------------------------------------------------------------------- /testprogs/structs_with_same_name.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main(){ 5 | { 6 | struct A{int f() {return 1;}}; 7 | cout< 2 | #include 3 | 4 | int main() { 5 | printf("one\n"); 6 | __builtin_debugtrap(); 7 | printf("two\n"); 8 | __asm__ volatile("int3"); 9 | printf("three\n"); 10 | raise(SIGTRAP); 11 | printf("four\n"); 12 | __builtin_trap(); 13 | } 14 | -------------------------------------------------------------------------------- /testprogs/types_across_units/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | BASEDIR=$(dirname "$0") 5 | gcc -g -O2 -c -o "$BASEDIR/a.o" "$BASEDIR/a.c" 6 | gcc -g -O2 -c -o "$BASEDIR/b.o" "$BASEDIR/b.c" 7 | gcc -g -O2 -c -o "$BASEDIR/main.o" "$BASEDIR/main.c" 8 | gcc -g -O2 -o "$BASEDIR/main" "$BASEDIR/main.o" "$BASEDIR/a.o" "$BASEDIR/b.o" 9 | -------------------------------------------------------------------------------- /testprogs/absl/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | BASEDIR=$(dirname "$0") 5 | 6 | mkdir -p "$BASEDIR/lib" 7 | if [ -z "$(ls -A "$BASEDIR/lib")" ] 8 | then 9 | git clone git@github.com:abseil/abseil-cpp.git "$BASEDIR/lib" 10 | fi 11 | mkdir -p "$BASEDIR/build" 12 | ( 13 | cd "$BASEDIR/build" 14 | cmake .. 15 | make -j8 containers 16 | ) 17 | -------------------------------------------------------------------------------- /testprogs/dlopen/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char **argv) { 5 | if (argc != 2) { 6 | fprintf(stderr, "usage: %s \n", argv[0]); 7 | return 1; 8 | } 9 | void *dl = dlopen(argv[1], RTLD_LAZY); 10 | void (*f)(int); 11 | f = dlsym(dl, "f"); 12 | (*f)(42); 13 | } 14 | -------------------------------------------------------------------------------- /testprogs/types_across_units/p.h: -------------------------------------------------------------------------------- 1 | // No compilation unit has both A and B defined. So the debugger can't get a full definition of Pair without relying on names and combining types from different units. 2 | 3 | struct A; 4 | struct B; 5 | 6 | struct Pair { 7 | struct A *a; 8 | struct B *b; 9 | }; 10 | 11 | void fa(struct Pair *p); 12 | void fb(struct Pair *p); 13 | -------------------------------------------------------------------------------- /testprogs/exception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void __attribute__((noinline)) f(int x) { 5 | throw std::string("hi"); 6 | } 7 | 8 | void __attribute__((noinline)) g(int a, int b) { 9 | f(a+b); 10 | } 11 | 12 | int main() { 13 | try { 14 | g(4, 5); 15 | } catch (std::string & e) { 16 | std::cout << e << std::endl; 17 | } 18 | 19 | g(2, 3); 20 | } 21 | -------------------------------------------------------------------------------- /other_things/questions_to_debugger_people.txt: -------------------------------------------------------------------------------- 1 | How's the 'breakpoint vs jump' distinguishing problem usually solved? Guesswork, or is there a reliably way? Spurious post-singlestep SIGTRAPs don't help. 2 | What's up with 70% of line program sequences starting at address 0 (DW_LNE_set_address 0)? Missing relocations? Intentional or compiler bug? See unrelocated_line_number_program_sequences.txt for examples. (Mostly in debug build, clang-16.) 3 | -------------------------------------------------------------------------------- /testprogs/anon_types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | volatile struct { 6 | int foo = 13; 7 | std::vector bar; 8 | } x; 9 | volatile auto y = [&](int z) { 10 | x.foo += z; 11 | }; 12 | volatile struct S { 13 | int a = 42; 14 | } s; 15 | std::cout << ((const char*)&x)[sizeof(x)-1] << ((const char*)&y)[sizeof(y)-1] << ((const char*)&s)[sizeof(s)-1]; 16 | } 17 | -------------------------------------------------------------------------------- /testprogs/string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | volatile const char * a = "asd"; 8 | std::string b = "qwe"; 9 | std::string_view c = b; 10 | std::string d(1000000, 'x'); 11 | volatile const char * e = d.c_str(); 12 | volatile size_t s = (size_t)std::strlen((const char*)a) + b.size() + c.size() + d.size() + (size_t)strlen((const char*)e); 13 | std::cout << s; 14 | } 15 | -------------------------------------------------------------------------------- /testprogs/many_threads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void do_something() { 6 | volatile long i = 0; 7 | while (i < 100000) { 8 | i = i + 1; 9 | if (i % 5 == 0) 10 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 11 | } 12 | } 13 | 14 | int main() { 15 | std::vector t; 16 | for (int i = 0; i < 10000; ++i) 17 | t.emplace_back([] { do_something(); }); 18 | for (auto & x : t) 19 | x.join(); 20 | } 21 | -------------------------------------------------------------------------------- /testprogs/string_rs.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::{OsStr, OsString}, path::{Path, PathBuf}}; 2 | 3 | fn main() { 4 | let a: &'static str = "asd"; 5 | let b: String = "qwe".to_string(); 6 | let c = OsStr::new("zxc"); 7 | let d = OsString::from("123"); 8 | let e = Path::new("rty"); 9 | let f = PathBuf::from("fgh"); 10 | let g = [10usize, 20, 30]; 11 | let h = &g[2..]; 12 | let s = a.len() + b.len() + c.len() + d.len() + e.as_os_str().len() + f.as_os_str().len() + g.len() + h.len(); 13 | std::hint::black_box(s); 14 | } 15 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | use crate::{*, executor::*, settings::*, util::*}; 2 | use std::{sync::Arc}; 3 | 4 | pub struct Context { 5 | pub settings: Settings, 6 | pub executor: Executor, 7 | // Wakes up the main thread, triggering a UI update. E.g. async search widget uses it to notify the UI of completion. 8 | pub wake_main_thread: Arc, 9 | } 10 | 11 | impl Context { 12 | pub fn invalid() -> Arc { Arc::new(Self {settings: Settings::default(), executor: Executor::invalid(), wake_main_thread: Arc::new(EventFD::new())}) } 13 | } 14 | -------------------------------------------------------------------------------- /testprogs/simple_threads.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | int main(){ 8 | cout<<"started"< 2 | #include 3 | 4 | static int count = 0; 5 | 6 | void signalHandler(int) { 7 | if (count == 0) 8 | printf("received signal\n"); 9 | count += 1; 10 | } 11 | 12 | int main() { 13 | struct sigaction sa = {0}; 14 | sa.sa_handler = signalHandler; 15 | if (sigaction(SIGUSR1, &sa, NULL) == -1) { 16 | perror("sigaction"); 17 | return 1; 18 | } 19 | 20 | while (count < 100000) 21 | raise(SIGUSR1); 22 | 23 | printf("bye\n"); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /other_things/cpuid.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | unsigned int eax, ebx, ecx, edx; 8 | unsigned int max_subleaf; 9 | __cpuid_count(0xD, 0, eax, ebx, ecx, edx); 10 | max_subleaf = eax; 11 | for (unsigned int i = 0; i <= max_subleaf; i++) { 12 | __cpuid_count(0xD, i, eax, ebx, ecx, edx); 13 | if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) continue; 14 | printf("Component %2u: Size=%u Offset=%u Flags=0x%08x\n", i, eax, ebx, ecx); 15 | } 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /testprogs/absl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(i_dont_want_to_use_cmake) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | set(CMAKE_BUILD_TYPE Debug) 8 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -Og") 9 | 10 | add_subdirectory(lib) 11 | 12 | add_executable(containers containers.cpp) 13 | 14 | target_link_libraries(containers 15 | absl::flat_hash_map 16 | absl::flat_hash_set 17 | absl::node_hash_map 18 | absl::node_hash_set 19 | absl::btree 20 | absl::inlined_vector 21 | absl::fixed_array 22 | ) 23 | -------------------------------------------------------------------------------- /testprogs/simple_mutex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main() { 7 | mutex m; 8 | 9 | auto f = [&] { 10 | for (int i = 0; i < 60; ++i) { 11 | { 12 | unique_lock lock(m); 13 | cout << i << endl; 14 | this_thread::sleep_for(chrono::seconds(1)); 15 | } 16 | this_thread::sleep_for(chrono::milliseconds(10)); 17 | } 18 | }; 19 | 20 | thread t1(f); 21 | thread t2(f); 22 | t1.join(); 23 | t2.join(); 24 | } 25 | -------------------------------------------------------------------------------- /testprogs/arrays.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main() { 7 | volatile int a[5] = {1, 2, 3, 4, 5}; 8 | volatile array b = {"a", "b", "c", "d"}; 9 | volatile long c[3][2] = {{1, 2}, {3, 4}, {5, 6}}; 10 | volatile int len = 10; 11 | volatile long d[len]; 12 | d[0] = 13; 13 | d[5] = 7; 14 | volatile int e[4][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}}; 15 | for (int i = 0; i < 4; ++i) 16 | for (int j = 0; j < 3; ++j) 17 | e[i][j][1] = e[i][j][1] + e[i][j][0] * 10; 18 | cout<<"hi"< 2 | 3 | enum class En : unsigned int { 4 | A = 1, 5 | B = 0xffffffff, 6 | C = 0, 7 | }; 8 | 9 | enum class Neg : int { 10 | A = -1, 11 | B = -2, 12 | C = -100, 13 | D = 5, 14 | }; 15 | 16 | enum Flags : int { 17 | A = 0x1, 18 | B = 0x2, 19 | C = 0x4, 20 | D = 0x8, 21 | A_AND_C = 0x5, 22 | }; 23 | 24 | int main() { 25 | std::array a {En::A, En::B, En::C, (En)42}; 26 | std::array b {(Neg)42, Neg::A, Neg::B, Neg::C, Neg::D}; 27 | volatile unsigned s = 0; 28 | for (auto x : a) s = s + (unsigned)x; 29 | for (auto x : b) s = s + (unsigned)x; 30 | (void)s; 31 | volatile int f = Flags::A | Flags::B | Flags::C; 32 | (void)f; 33 | } 34 | -------------------------------------------------------------------------------- /about.hbs: -------------------------------------------------------------------------------- 1 | // This file is generated by `cargo about`, don't edit it manually. 2 | // See release.sh for the command line. 3 | 4 | pub const LICENSES_DOC: &'static str = r###" 5 | nnd is licensed under the Apache License, Version 2.0. 6 | Source code is available at https://github.com/al13n321/nnd 7 | It uses open source libraries under various licenses. 8 | 9 | Overview: 10 | {{#each overview}} 11 | * {{{name}}} ({{{count}}}) 12 | {{/each}} 13 | 14 | License texts: 15 | {{#each licenses}} 16 | 17 | 18 | ======================== {{name}} ======================== 19 | 20 | Used by: 21 | {{#each used_by}} 22 | * {{{crate.name}}} {{{crate.version}}} 23 | {{#if crate.repository}}{{{crate.repository}}}{{else}}https://crates.io/crates/{{{crate.name}}}{{/if}} 24 | {{/each}} 25 | 26 | 27 | {{{text}}} 28 | {{/each}} 29 | "###; 30 | -------------------------------------------------------------------------------- /testprogs/types_rs.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use std::hint::black_box; 3 | 4 | enum E { 5 | X, 6 | Y(usize), 7 | Z {a: String, b: usize}, 8 | W, 9 | A(usize, usize), 10 | } 11 | 12 | enum F { 13 | X, 14 | Y, 15 | Z, 16 | W, 17 | } 18 | 19 | fn main() { 20 | let x = E::X; 21 | let y = E::Y(42); 22 | let z = E::Z {a: "hi".to_string(), b: 13}; 23 | let w = E::W; 24 | let a = E::A(10, 20); 25 | black_box((x, y, z, w, a)); 26 | 27 | let mut a = [E::X, E::W, E::Y(1), E::Y(2), E::Z {a: "meow".to_string(), b: 3}]; 28 | let s = &a[1..3]; 29 | black_box(s); 30 | let s = &mut a[1..3]; 31 | black_box(s); 32 | black_box(a); 33 | 34 | let t = (E::X, 1337, "asd", E::Y(1)); 35 | black_box(t); 36 | 37 | let fs = [F::X, F::Y, F::Z, F::W]; 38 | black_box(fs); 39 | } 40 | -------------------------------------------------------------------------------- /testprogs/global_variables.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int __attribute__((noinline)) f(int x) { 5 | int v = 1; 6 | const int C = 10; 7 | constexpr int CE = 20; 8 | static int S = 30; 9 | static const int SC = 40; 10 | static constexpr int SCE = 50; 11 | return x + v + C + CE + S + SC + SCE; 12 | } 13 | 14 | inline int g(int x) { 15 | int v = 1; 16 | const int C = 10; 17 | constexpr int CE = 20; 18 | static int S = 30; 19 | static const int SC = 40; 20 | static constexpr int SCE = 50; 21 | return x + v + C + CE + S + SC + SCE; 22 | } 23 | 24 | int G = 99; 25 | const int GC = 100; 26 | constexpr int GCE = 200; 27 | static int GS = 300; 28 | static const int GSC = 400; 29 | static constexpr int GSCE = 500; 30 | 31 | struct str { 32 | static int NS; 33 | static const int NSC = 4000; 34 | static constexpr int NSCE = 5000; 35 | }; 36 | int str::NS = 3000; 37 | 38 | constexpr std::string_view GSV = "meow"; 39 | 40 | template 41 | struct T { 42 | int f = X; 43 | static constexpr int XX = X; 44 | }; 45 | 46 | int main() { 47 | volatile T<1337> t; 48 | printf("%d", f(42) + g(42) + t.f + T<>::XX + G + GC + GCE + GS + GSC + GSCE); 49 | } 50 | -------------------------------------------------------------------------------- /testprogs/function_and_member_pointers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | long f(int x) { 6 | return x + 1; 7 | } 8 | long g(int x) { 9 | return x * 2; 10 | } 11 | typedef long F(int x); 12 | 13 | struct S { 14 | int a=10,b=20,c=30,d=40; 15 | 16 | void foo() volatile { 17 | cout<<"foo "< f2 = [&] { 39 | p = q; 40 | }; 41 | 42 | volatile S s; 43 | int S::*volatile bp = &S::b; 44 | volatile SF cp = &S::c; 45 | cout << s.*bp << ' ' << s.*cp << endl; 46 | s.*bp += 2; 47 | s.*cp -= 2; 48 | cout << s.b << ' ' << s.c << endl; 49 | 50 | void (S::*volatile mfp)() volatile = &S::foo; 51 | (s.*mfp)(); 52 | mfp = &S::bar; 53 | (s.*mfp)(); 54 | 55 | volatile fn ffn; 56 | } 57 | -------------------------------------------------------------------------------- /testprogs/signal_handler_with_threads.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void signalHandler(int) { 9 | volatile int i = 42; 10 | i = i + 27; 11 | } 12 | 13 | void* noOpLoop(void* arg) { 14 | volatile int i = 0; 15 | while (1) { 16 | i = i + 1; 17 | } 18 | return NULL; 19 | } 20 | 21 | void* signalSender(void* arg) { 22 | pthread_t target_thread = *((pthread_t*)arg); 23 | while (1) { 24 | pthread_kill(target_thread, SIGUSR1); 25 | usleep(10000); 26 | } 27 | return NULL; 28 | } 29 | 30 | int main() { 31 | if (signal(SIGUSR1, signalHandler) == SIG_ERR) { 32 | printf("error setting signal handler.\n"); 33 | return 1; 34 | } 35 | 36 | pthread_t thread1, thread2; 37 | 38 | if (pthread_create(&thread1, NULL, noOpLoop, NULL) != 0) { 39 | printf("error creating thread.\n"); 40 | return 1; 41 | } 42 | 43 | if (pthread_create(&thread2, NULL, signalSender, &thread1) != 0) { 44 | printf("error creating thread.\n"); 45 | return 1; 46 | } 47 | 48 | pthread_join(thread1, NULL); 49 | pthread_join(thread2, NULL); 50 | 51 | printf("???\n"); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /testprogs/simple_codegen.c: -------------------------------------------------------------------------------- 1 | // (written mostly by Claude) 2 | 3 | #define _DEFAULT_SOURCE 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Function pointer type for our dynamically created function 11 | typedef int (*MulFunc)(int); 12 | 13 | int main() { 14 | // Machine code for a function that multiplies its argument by 2 15 | // This is x64 assembly: mov eax, edi; add eax, eax; ret 16 | unsigned char code[] = {0x89, 0xf8, 0x01, 0xc0, 0xc3}; 17 | size_t code_size = sizeof(code); 18 | 19 | // Allocate executable memory using mmap 20 | void *mem = mmap(NULL, code_size, PROT_READ | PROT_WRITE | PROT_EXEC, 21 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 22 | 23 | if (mem == MAP_FAILED) { 24 | perror("mmap"); 25 | exit(1); 26 | } 27 | 28 | // Copy our machine code to the allocated memory 29 | memcpy(mem, code, code_size); 30 | 31 | // Cast the memory to our function pointer type 32 | MulFunc multiply = (MulFunc)mem; 33 | 34 | // Test our function 35 | int result = multiply(5); 36 | printf("5 * 2 = %d\n", result); 37 | 38 | // Clean up 39 | if (munmap(mem, code_size) == -1) { 40 | perror("munmap"); 41 | exit(1); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /testprogs/panic.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::hint::black_box; 3 | use std::panic; 4 | 5 | fn trigger_panic(method: usize) { 6 | match method { 7 | 0 => panic!("explicit panic"), 8 | 1 => { 9 | let a = black_box(u32::MAX); 10 | let b = black_box(1u32); 11 | let _c = a + b; 12 | } 13 | 2 => { 14 | let arr = black_box([1, 2, 3]); 15 | let idx = black_box(10usize); 16 | let _x = arr[idx]; 17 | } 18 | 3 => { 19 | let x = black_box(false); 20 | assert!(x, "assertion failed"); 21 | } 22 | 4 => { 23 | let x = black_box(false); 24 | debug_assert!(x, "debug assertion failed"); 25 | } 26 | 5 => { 27 | let a = black_box(10i32); 28 | let b = black_box(0i32); 29 | let _c = a / b; 30 | } 31 | _ => {} 32 | } 33 | } 34 | 35 | fn main() { 36 | let args: Vec = env::args().collect(); 37 | 38 | let catch = args.get(1) 39 | .and_then(|s| s.parse::().ok()) 40 | .unwrap_or(1) == 1; 41 | 42 | if let Some(method_str) = args.get(2) { 43 | if let Ok(method) = method_str.parse::() { 44 | if catch { 45 | let _ = panic::catch_unwind(|| trigger_panic(method)); 46 | } else { 47 | trigger_panic(method); 48 | } 49 | } 50 | } else { 51 | for i in 0..6 { 52 | if catch { 53 | let _ = panic::catch_unwind(|| trigger_panic(i)); 54 | } else { 55 | trigger_panic(i); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /testprogs/simple_tls.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | thread_local int simple_int = 42; 7 | thread_local int simple_array[5] = {10, 20, 30, 40, 50}; 8 | thread_local std::vector tls_vector = {1, 2, 3}; 9 | 10 | std::mutex cout_mutex; 11 | 12 | void thread_function(int thread_id) { 13 | // Modify the thread-local variables to prevent optimization 14 | simple_int += thread_id; 15 | simple_array[0] += thread_id; 16 | tls_vector.push_back(thread_id); 17 | 18 | // Print the values to show they are thread-specific 19 | std::lock_guard lock(cout_mutex); 20 | std::cout << "Thread " << thread_id << ":\n"; 21 | std::cout << " simple_int = " << simple_int << "\n"; 22 | std::cout << " simple_array[0] = " << simple_array[0] << "\n"; 23 | std::cout << " tls_vector = {"; 24 | for (size_t i = 0; i < tls_vector.size(); ++i) { 25 | if (i > 0) std::cout << ", "; 26 | std::cout << tls_vector[i]; 27 | } 28 | std::cout << "}\n"; 29 | } 30 | 31 | int main() { 32 | std::thread t1(thread_function, 1); 33 | std::thread t2(thread_function, 2); 34 | std::thread t3(thread_function, 3); 35 | 36 | t1.join(); 37 | t2.join(); 38 | t3.join(); 39 | 40 | // Access in main thread to prevent optimization 41 | simple_int += 100; 42 | simple_array[1] += 100; 43 | tls_vector.push_back(100); 44 | 45 | std::cout << "Main thread:\n"; 46 | std::cout << " simple_int = " << simple_int << "\n"; 47 | std::cout << " simple_array[1] = " << simple_array[1] << "\n"; 48 | std::cout << " tls_vector = {"; 49 | for (size_t i = 0; i < tls_vector.size(); ++i) { 50 | if (i > 0) std::cout << ", "; 51 | std::cout << tls_vector[i]; 52 | } 53 | std::cout << "}\n"; 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /other_things/debug_registers_experiment.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define CALL(x) if (x < 0) { perror(#x); exit(1); } 13 | 14 | int main() { 15 | int pid = fork(); 16 | if (pid < 0) { perror("fork"); exit(1); } 17 | if (pid == 0) { 18 | printf("hi\n"); 19 | __asm__("mov $0, %rax\n" 20 | "loop:\n" 21 | "inc %rax\n" 22 | "jmp loop\n"); 23 | printf("bye?\n"); 24 | return 1; 25 | } 26 | 27 | CALL(ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_EXITKILL | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEFORK | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE)); 28 | 29 | sleep(1); 30 | 31 | CALL(ptrace(PTRACE_INTERRUPT, pid, 0, 0)); 32 | 33 | int assigned = 0; 34 | 35 | for (int i = 0; i < 30; ++i) { 36 | int w; 37 | CALL(wait(&w)); 38 | 39 | if (!WIFSTOPPED(w)) { fprintf(stderr, "unexpected wait result: %d", w); exit(1); } 40 | int sig = WSTOPSIG(w); 41 | if (sig != SIGTRAP) { fprintf(stderr, "unexpected signal: %d", sig); exit(1); } 42 | 43 | struct user_regs_struct regs; 44 | CALL(ptrace(PTRACE_GETREGS, pid, 0, ®s)); 45 | 46 | printf("rip: %llu, rax: %llu\n", regs.rip, regs.rax); 47 | 48 | //if (!assigned) { 49 | CALL(ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[0]), regs.rip)); 50 | CALL(ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[7]), 1 << 0)); 51 | //} 52 | 53 | //CALL(ptrace(PTRACE_SINGLESTEP, pid, 0, 0)); 54 | CALL(ptrace(PTRACE_CONT, pid, 0, 0)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /testprogs/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | #set -x 4 | 5 | BASEDIR=$(dirname "$0") 6 | 7 | if [ ! -d "$BASEDIR/build" ] 8 | then 9 | mkdir "$BASEDIR/build" 10 | fi 11 | 12 | build_file() { 13 | local file="$1" 14 | local name_without_extension="${file%.*}" 15 | local extension="${file##*.}" 16 | 17 | case "$extension" in 18 | c) 19 | echo "$file" 20 | clang -g -O1 -std=c23 -pthread -mavx -mavx2 -o "$BASEDIR/build/$name_without_extension" "$BASEDIR/$file" 21 | ;; 22 | cpp) 23 | echo "$file" 24 | FLAGS="-g -O1 -std=c++20 -Wno-vla-cxx-extension -mavx -mavx2" 25 | clang++ -stdlib=libc++ $FLAGS -o "$BASEDIR/build/$name_without_extension-libc++" "$BASEDIR/$file" 26 | clang++ -stdlib=libstdc++ $FLAGS -o "$BASEDIR/build/$name_without_extension" "$BASEDIR/$file" 27 | ;; 28 | rs) 29 | echo "$file" 30 | rustc --edition=2021 -g -o "$BASEDIR/build/$name_without_extension" "$BASEDIR/$file" 31 | ;; 32 | zig) 33 | echo "$file" 34 | zig build-exe -femit-bin="$BASEDIR/build/$name_without_extension" "$BASEDIR/$file" 35 | zig build-exe -fno-llvm -fno-lld -femit-bin="$BASEDIR/build/${name_without_extension}-nollvm" "$BASEDIR/$file" 36 | rm -f "$BASEDIR/build/${name_without_extension}.o" "$BASEDIR/build/${name_without_extension}-nollvm.o" 37 | ;; 38 | esac 39 | } 40 | 41 | if [ $# -gt 0 ]; then 42 | for filename in "$@"; do 43 | p="$BASEDIR/$filename" 44 | if ! [ -f "$p" ] 45 | then 46 | echo "file $p not found" 47 | exit 1 48 | fi 49 | build_file "$(basename "$filename")" 50 | done 51 | else 52 | for file in "$BASEDIR"/*; do 53 | filename=$(basename "$file") 54 | build_file "$filename" 55 | done 56 | fi 57 | -------------------------------------------------------------------------------- /testprogs/simple_coro.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct Coroutine { 7 | struct promise_type { 8 | std::suspend_always initial_suspend() { return {}; } 9 | std::suspend_always final_suspend() noexcept { return {}; } 10 | void return_void() {} 11 | void unhandled_exception() {} 12 | std::suspend_always yield_value(int) { return {}; } 13 | 14 | Coroutine get_return_object() { 15 | return Coroutine{std::coroutine_handle::from_promise(*this)}; 16 | } 17 | }; 18 | 19 | std::coroutine_handle handle; 20 | 21 | Coroutine(std::coroutine_handle h) : handle(h) {} 22 | Coroutine(Coroutine && c) : handle(std::exchange(c.handle, nullptr)) {} 23 | ~Coroutine() { if (handle) handle.destroy(); } 24 | }; 25 | 26 | Coroutine a_coroutine_function() { 27 | std::cout << "1" << std::endl; 28 | co_await std::suspend_always{}; 29 | std::cout << "3" << std::endl; 30 | co_await std::suspend_always{}; 31 | std::cout << "5" << std::endl; 32 | } 33 | 34 | struct ManualExecutor { 35 | Coroutine coroutine; 36 | 37 | ManualExecutor(Coroutine coro) : coroutine(std::move(coro)) {} 38 | 39 | bool step() { 40 | if (!coroutine.handle.done()) { 41 | coroutine.handle.resume(); 42 | return true; 43 | } 44 | return false; 45 | } 46 | }; 47 | 48 | int main() { 49 | { 50 | ManualExecutor exec(a_coroutine_function()); 51 | std::cout << "0" << std::endl; 52 | if (!exec.step()) std::abort(); 53 | std::cout << "2" << std::endl; 54 | if (!exec.step()) std::abort(); 55 | std::cout << "4" << std::endl; 56 | if (!exec.step()) std::abort(); 57 | std::cout << "6" << std::endl; 58 | if (exec.step()) std::abort(); 59 | std::cout << "7" << std::endl; 60 | } 61 | std::cout << "8 - done" << std::endl; 62 | } 63 | -------------------------------------------------------------------------------- /testprogs/simple_async_rs.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll, Waker, RawWaker, RawWakerVTable}, 5 | }; 6 | 7 | struct ManualExecutor { 8 | future: Pin>>, 9 | } 10 | 11 | impl ManualExecutor { 12 | fn run(future: impl Future + 'static) -> Self { 13 | Self { 14 | future: Box::pin(future), 15 | } 16 | } 17 | 18 | fn step(&mut self) -> bool { 19 | let waker = unsafe { Waker::from_raw(dummy_raw_waker()) }; 20 | let mut cx = Context::from_waker(&waker); 21 | 22 | match self.future.as_mut().poll(&mut cx) { 23 | Poll::Ready(()) => false, 24 | Poll::Pending => true, 25 | } 26 | } 27 | } 28 | 29 | fn dummy_raw_waker() -> RawWaker { 30 | unsafe fn clone(_: *const ()) -> RawWaker { 31 | dummy_raw_waker() 32 | } 33 | unsafe fn wake(_: *const ()) {} 34 | unsafe fn wake_by_ref(_: *const ()) {} 35 | unsafe fn drop(_: *const ()) {} 36 | 37 | RawWaker::new(std::ptr::null(), &RawWakerVTable::new(clone, wake, wake_by_ref, drop)) 38 | } 39 | 40 | struct Suspend { 41 | polled: bool, 42 | } 43 | 44 | impl Future for Suspend { 45 | type Output = (); 46 | 47 | fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { 48 | if self.polled { 49 | Poll::Ready(()) 50 | } else { 51 | self.polled = true; 52 | Poll::Pending 53 | } 54 | } 55 | } 56 | 57 | fn suspend() -> Suspend { 58 | Suspend {polled: false} 59 | } 60 | 61 | async fn an_async_function() { 62 | println!("1"); 63 | suspend().await; 64 | println!("3"); 65 | suspend().await; 66 | println!("5"); 67 | } 68 | 69 | fn main() { 70 | let mut exec = ManualExecutor::run(an_async_function()); 71 | println!("0"); 72 | assert!(exec.step()); 73 | println!("2"); 74 | assert!(exec.step()); 75 | println!("4"); 76 | assert!(!exec.step()); 77 | println!("6 - done"); 78 | } 79 | 80 | -------------------------------------------------------------------------------- /other_things/hashmap_vs_sort_bench.rs: -------------------------------------------------------------------------------- 1 | use rand; // 0.8.5 2 | use rand::{Rng, distributions::Alphanumeric}; 3 | use std::{time::Instant, collections::HashMap, collections::hash_map::DefaultHasher, hash::{Hash, Hasher}, ops::Range}; 4 | 5 | fn main() { 6 | let prep_start = Instant::now(); 7 | let mut rng = rand::thread_rng(); 8 | let mut pool: String = String::new(); 9 | let mut ranges: Vec> = Vec::new(); 10 | for _ in 0..10000000 { 11 | let start = pool.len(); 12 | let len = rng.gen_range(1..20); 13 | for _ in 0..len { 14 | pool.push(rng.sample(Alphanumeric) as char); 15 | } 16 | ranges.push(start..pool.len()); 17 | } 18 | let mut strs: Vec<&str> = ranges.iter().map(|r| pool.get(r.clone()).unwrap()).collect(); 19 | 20 | let hash_start = Instant::now(); 21 | println!("prep: {:.3}ms, len: {:.3}M", (hash_start - prep_start).as_millis(), pool.len() as f64 / 1e6); 22 | 23 | let mut hashes: Vec<(u64, [usize; 4])> = Vec::with_capacity(strs.len()); 24 | for s in strs.iter().copied() { 25 | let mut hasher = DefaultHasher::new(); 26 | s.hash(&mut hasher); 27 | hashes.push((hasher.finish(), [0; 4])); 28 | } 29 | 30 | let map_start = Instant::now(); 31 | println!("hash: {:.3}ms", (map_start - hash_start).as_millis()); 32 | 33 | let mut map: HashMap<&str, usize> = HashMap::new(); 34 | for s in strs.iter().copied() { 35 | let len = map.len(); 36 | map.entry(s).or_insert(len); 37 | } 38 | 39 | let sort_start = Instant::now(); 40 | println!("map: {:.3}ms, len: {:.3}M", (sort_start - map_start).as_millis(), map.len() as f64 / 1e6); 41 | 42 | strs.sort_unstable(); 43 | let hsort_start = Instant::now(); 44 | println!("sort: {:.3}ms, first: {}, last: {}", (hsort_start - sort_start).as_millis(), strs[0], strs.last().unwrap()); 45 | 46 | hashes.sort_unstable_by_key(|h| h.0); 47 | let main_end = Instant::now(); 48 | println!("hsort: {:.3}ms, first: {}, last: {}", (main_end - hsort_start).as_millis(), hashes[0].0, hashes.last().unwrap().0); 49 | } 50 | -------------------------------------------------------------------------------- /src/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::{Arc, Mutex, Condvar}, thread::{self, JoinHandle}, collections::VecDeque}; 2 | 3 | pub struct Executor { 4 | pub num_threads: usize, 5 | shared: Arc, 6 | threads: Vec>, 7 | } 8 | 9 | impl Executor { 10 | pub fn invalid() -> Self { Self {num_threads: 0, shared: Arc::new(Shared {tasks: Mutex::new(VecDeque::new()), wake_workers: Condvar::new()}), threads: Vec::new()} } 11 | 12 | pub fn new(num_threads: usize) -> Self { 13 | assert!(num_threads > 0); 14 | let mut exec = Self {num_threads, shared: Arc::new(Shared {tasks: Mutex::new(VecDeque::new()), wake_workers: Condvar::new()}), threads: Vec::new()}; 15 | for i in 0..num_threads { 16 | let shared_clone = exec.shared.clone(); 17 | exec.threads.push(thread::Builder::new().name("debworker".into()).spawn(|| Self::worker_thread(shared_clone)).unwrap()); 18 | } 19 | exec 20 | } 21 | 22 | pub fn add_boxed(&self, f: Box) { 23 | self.shared.tasks.lock().unwrap().push_back(Task {f}); 24 | self.shared.wake_workers.notify_one(); 25 | } 26 | 27 | pub fn add(&self, f: F) { 28 | self.add_boxed(Box::new(f)); 29 | } 30 | 31 | fn worker_thread(shared: Arc) { 32 | loop { 33 | let task; 34 | { 35 | let mut lock = shared.tasks.lock().unwrap(); 36 | while lock.is_empty() { 37 | lock = shared.wake_workers.wait(lock).unwrap(); 38 | } 39 | task = lock.pop_front().unwrap(); 40 | } 41 | (task.f)(); 42 | } 43 | } 44 | } 45 | 46 | impl Drop for Executor { 47 | fn drop(&mut self) { 48 | // Currently we only have one Executor, which lives until the end of the program. 49 | // So we don't join threads, that would just add a few seconds of delay when exiting the program sometimes, for no benefit. 50 | } 51 | } 52 | 53 | struct Task { 54 | f: Box, 55 | } 56 | 57 | struct Shared { 58 | // We do at most hundreds of tasks per second, so not implementing anything fancy here, and not worrying about the cost of having lots of Arc-s and pointer chasing (here and in SymbolsRegistry, etc). 59 | tasks: Mutex>, 60 | wake_workers: Condvar, 61 | } 62 | -------------------------------------------------------------------------------- /testprogs/absl/containers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "absl/container/flat_hash_map.h" 6 | #include "absl/container/flat_hash_set.h" 7 | #include "absl/container/node_hash_map.h" 8 | #include "absl/container/node_hash_set.h" 9 | #include "absl/container/btree_map.h" 10 | #include "absl/container/btree_set.h" 11 | #include "absl/container/inlined_vector.h" 12 | #include "absl/container/fixed_array.h" 13 | 14 | std::string random_string(std::mt19937& gen, int len) { 15 | static const char alphanum[] = 16 | "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 17 | std::uniform_int_distribution<> dis(0, sizeof(alphanum) - 2); 18 | std::string str(len, ' '); 19 | for (int i = 0; i < len; ++i) { 20 | str[i] = alphanum[dis(gen)]; 21 | } 22 | return str; 23 | } 24 | 25 | int main() { 26 | std::mt19937 gen(42); 27 | std::uniform_int_distribution<> dis(1, 1000); 28 | 29 | absl::flat_hash_map fhm; 30 | for (int i = 0; i < 200; ++i) { 31 | fhm[i] = random_string(gen, 10); 32 | } 33 | 34 | absl::flat_hash_set fhs; 35 | for (int i = 0; i < 200; ++i) { 36 | fhs.insert(dis(gen)); 37 | } 38 | 39 | absl::node_hash_map nhm; 40 | for (int i = 0; i < 200; ++i) { 41 | nhm[i] = random_string(gen, 10); 42 | } 43 | 44 | absl::node_hash_set nhs; 45 | for (int i = 0; i < 200; ++i) { 46 | nhs.insert(dis(gen)); 47 | } 48 | 49 | absl::btree_map btm; 50 | for (int i = 0; i < 200; ++i) { 51 | btm[i] = random_string(gen, 10); 52 | } 53 | 54 | absl::btree_set bts; 55 | for (int i = 0; i < 200; ++i) { 56 | bts.insert(dis(gen)); 57 | } 58 | 59 | absl::InlinedVector small_iv; 60 | for (int i = 0; i < 5; ++i) { 61 | small_iv.push_back(dis(gen)); 62 | } 63 | 64 | absl::InlinedVector large_iv; 65 | for (int i = 0; i < 20; ++i) { 66 | large_iv.push_back(dis(gen)); 67 | } 68 | 69 | absl::FixedArray fa(50); 70 | for (int i = 0; i < 50; ++i) { 71 | fa[i] = dis(gen); 72 | } 73 | 74 | volatile int dummy = fhm.size() + fhs.size() + nhm.size() + nhs.size() + 75 | btm.size() + bts.size() + small_iv.size() + large_iv.size() + 76 | fa.size(); 77 | (void)dummy; 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A debugger for Linux. Partially inspired by RemedyBG. 2 | 3 | Mom, can we have RAD Debugger on Linux? 4 | No, we have debugger at home. 5 | Debugger at home: 6 | 7 | ![screenshot](https://github.com/user-attachments/assets/e0b03f1e-c1d1-4e38-a992-2ace7321bb75) 8 | 9 | Properties: 10 | * Fast. 11 | * TUI. 12 | * Not based on gdb or lldb, implemented mostly from scratch. 13 | * Works on large executables. 14 | 15 | What we mean by "fast": 16 | * Operations that can be instantaneous should be instantaneous. I.e. snappy UI, no random freezes, no long waits. 17 | (Known exception: if the program has >~2k threads things become pretty slow. This will be improved.) 18 | * Operations that can't be instantaneous (loading debug info, searching for functions and types) should be reasonably efficient, multi-threaded, asynchronous, cancellable, and have progress bars. 19 | 20 | Limitations: 21 | * Linux only 22 | * x86 only 23 | * 64-bit only 24 | * for native code only (e.g. C++, Rust, Zig, Odin, not Java or Python) 25 | * TUI only (no REPL, GUI, or IDE integration) 26 | * no remote debugging (but works fine over ssh) 27 | * single process (doesn't follow forks) 28 | * no record/replay or backwards stepping 29 | 30 | Development status: 31 | * Most standard debugger features are there. E.g. breakpoints, conditional breakpoints, data breakpoints, stepping of all kinds, showing code and disassembly, watch expressions, built-in pretty-printers for most of C++ and Rust standard library. Many quality-of-life features are there (e.g. auto-downcasting abstract classes to concrete classes based on vtable). But I'm sure there are lots of missing features that I never needed but other people consider essential; let me know. 32 | * I use it every day and find it very helpful. 33 | * Not in active development right now. I fix reported bugs and add small requested features, but likely won't get around to implementing big features soon (e.g. redesigning the watch expression language to have loops etc, ARM support, Mac OS support, GUI, DAP). 34 | 35 | Distributed as a single 6 MB executable file with no dependencies. 36 | 37 | "Installation": 38 | ```bash 39 | curl -L -o nnd 'https://github.com/al13n321/nnd/releases/latest/download/nnd' 40 | chmod +x nnd 41 | # try `./nnd --help` to get started 42 | ``` 43 | 44 | Or build from source: 45 | ```bash 46 | rustup toolchain install 1.89 47 | cargo +1.89.0 build --profile dbgo 48 | # The executable is at target/dbgo/nnd 49 | ``` 50 | 51 | Run `nnd --help` for documentation. 52 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nnd" 3 | license = "Apache-2.0" 4 | description = "A fast handmade TUI-based debugger for Linux" 5 | documentation = "https://github.com/al13n321/nnd" 6 | repository = "https://github.com/al13n321/nnd" 7 | keywords = ["debugger", "tui"] 8 | categories = ["development-tools::debugging"] 9 | version = "0.65.0" # [[this version number is written by release.sh]] 10 | edition = "2021" # I tried updating to 2024, but it just makes build 10% slower without making the resulting executable any faster 11 | rust-version = "1.89" # build often breaks when updating the compiler because of newly added warnings 12 | 13 | [profile.dev] 14 | panic = 'abort' 15 | opt-level = 1 16 | 17 | [profile.release] 18 | panic = 'abort' 19 | 20 | [profile.dbgo] 21 | inherits = "release" 22 | debug = true 23 | debug-assertions = true 24 | overflow-checks = true 25 | 26 | [profile.release-with-debug-info] 27 | inherits = "release" 28 | debug = true 29 | 30 | [dependencies] 31 | libc = "0.2.146" # syscall wrappers 32 | iced-x86 = {version = "1.19", default-features = false, features = ["std", "decoder", "instr_info", "nasm"]} # disassember 33 | bitflags = "^1.3" # TODO: update to 2.* 34 | gimli = {version = "^0.31.1", default-features = false, features = ["std", "read"]} # TODO: remove the remaining usage (e.g. evaluating line number programs and dwarf expressions) and delet dis 35 | #gimli = {path = "/home/al13n/Dropbox/coding/gimli"} 36 | crc32fast = "^1.3.2" # a few things in ELF and DWARF use crc32 37 | flate2 = "^1.0.26" # for decompressing ELF sections 38 | md5 = "^0.7" # for checking source file hashes against debug info 39 | cpp_demangle = "^0.5.1" # for demangling function names (unavoidable in .symtab, convenient in .debug_info); TODO: update after https://github.com/gimli-rs/cpp_demangle/pull/306 is released 40 | #cpp_demangle = {path = "/home/al13n/cpp_demangle"} 41 | rand = "^0.8.5" 42 | unicode-segmentation = "^1.10" 43 | unicode-width = "^0.1" 44 | rustc-demangle = "^0.1" 45 | # HTTP+TLS client for downloading from debuginfod. This accounts for more than half of the dependency tree (see `cargo tree`) 46 | # and adds the only non-cargo dependency: musl-tools (use e.g. `apt install musl-tools`). But I couldn't find anything smaller :( 47 | ureq = "^3.0" 48 | voracious_radix_sort = "^1.2" 49 | 50 | # musl's default allocator is very slow with many threads because of mutex contention. 51 | [target.'cfg(target_env = "musl")'.dependencies] 52 | #rlsf = "^0.2" # symbols loading 4% slower than jemalloc 53 | tikv-jemallocator = "^0.6" 54 | 55 | [dev-dependencies] 56 | gimli = {version = "^0.31.1", default-features = false, features = ["std", "read", "write"]} 57 | -------------------------------------------------------------------------------- /other_things/echo_input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Save original termios settings 11 | struct termios orig_termios; 12 | 13 | // Function to restore the original terminal settings 14 | void restore_terminal() { 15 | tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); 16 | printf("\033[?1049l"); // Switch back to main screen 17 | printf("\033[?25h"); // Show cursor 18 | printf("\x1b[?9l\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?1007l\x1b[?1015l\x1b[?1016l\x1b[?1004l"); 19 | fflush(stdout); 20 | } 21 | 22 | // Function to switch to alternate screen and raw mode 23 | void setup_terminal() { 24 | tcgetattr(STDIN_FILENO, &orig_termios); 25 | struct termios raw = orig_termios; 26 | cfmakeraw(&raw); 27 | tcsetattr(STDIN_FILENO, TCSANOW, &raw); 28 | printf("\033[?1049h"); // Switch to alternate screen 29 | printf("\033[H\033[J"); // Clear screen 30 | printf("\033[?25l"); // Hide cursor 31 | // https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking 32 | printf("\x1b[?1003h\x1b[?1007h\x1b[?1006h\x1b[?1004h"); 33 | fflush(stdout); 34 | } 35 | 36 | // Function to get the terminal height 37 | int get_terminal_height() { 38 | struct winsize ws; 39 | ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); 40 | return ws.ws_row; 41 | } 42 | 43 | // Function to print the buffer with escaped non-alphanumeric characters 44 | void print_buffer(const char *buffer, ssize_t len) { 45 | for (ssize_t i = 0; i < len; ++i) { 46 | if (buffer[i] == '\e') { 47 | printf("\\e"); 48 | } else if (buffer[i] >= 33 && buffer[i] <= 126) { 49 | putchar(buffer[i]); 50 | } else { 51 | printf("\\x%02x", (unsigned char)buffer[i]); 52 | } 53 | } 54 | putchar('\n'); 55 | fflush(stdout); 56 | } 57 | 58 | int main() { 59 | setup_terminal(); 60 | atexit(restore_terminal); // Ensure terminal settings are restored on exit 61 | 62 | struct pollfd fds = { 63 | .fd = STDIN_FILENO, 64 | .events = POLLIN 65 | }; 66 | 67 | char buffer[128]; 68 | int row = 0; 69 | int max_row = get_terminal_height(); 70 | 71 | while (1) { 72 | if (poll(&fds, 1, -1) > 0) { 73 | if (fds.revents & POLLIN) { 74 | ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer) - 1); 75 | if (n > 0) { 76 | buffer[n] = '\0'; 77 | if (buffer[0] == 'q') { 78 | break; 79 | } 80 | printf("\033[%d;1H", row + 1); // Move cursor to the start of the next line 81 | print_buffer(buffer, n); 82 | row++; 83 | if (row >= max_row) { 84 | printf("\033[2J\033[H"); // Clear screen and move cursor to top 85 | fflush(stdout); 86 | row = 0; 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /other_things/poketext_experiment.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | using namespace std; 14 | 15 | #define CHECK(x) if (!(x)) { cerr << "check failed on line " << __LINE__ << ": " << #x << endl; exit(1); } 16 | #define CHECK_EQ(a, b) { auto hyg_a = (a); auto hyg_b = (b); if (hyg_a != hyg_b) { cerr << "check failed on line " << __LINE__ << ": " #a " == " #b " (" << hyg_a << " != " << hyg_b << ")" << endl; exit(1); } } 17 | #define CALL(x) if ((x) < 0) { perror(#x); exit(1); } 18 | 19 | int main() { 20 | //cout<> 16, PTRACE_EVENT_CLONE); 45 | pid_t tid; 46 | CALL(ptrace(PTRACE_GETEVENTMSG, pid, 0, &tid)); 47 | cout<<"thread "<>16, PTRACE_EVENT_STOP); 55 | // We get either SIGTRAP or SIGSTOP, seemingly at random, different from run to run. 56 | CHECK(WSTOPSIG(w) == SIGTRAP || WSTOPSIG(w) == SIGSTOP); 57 | 58 | //CALL(ptrace(PTRACE_CONT, tid, 0, 0)); cout<<"continued "<>16, 0); 92 | 93 | cout<<"all done"</dev/null 57 | then 58 | TAG_NUM=$(($TAG_NUM + 1)) 59 | TAG="v0.$TAG_NUM" 60 | NEW_GH_TAG=1 61 | fi 62 | 63 | # Write version number to Cargo.toml 64 | BASEDIR=$(dirname "$0") 65 | sed -i.bak "s/^version = \"0\.[0-9]\+\.0\" # \[\[this version number is written by release\.sh\]\]$/version = \"0.$TAG_NUM.0\" # [[this version number is written by release.sh]]/" "$BASEDIR/Cargo.toml" && rm "$BASEDIR/Cargo.toml.bak" 66 | 67 | # Pass build time to the env!() in main.rs, passed-through by cargo. 68 | export NND_BUILD_TIME=`date --utc +"%Y-%m-%d %H:%M:%S-%Z"` 69 | 70 | # Generate boring legal info. This has to be after updating version number above. 71 | cargo about generate about.hbs -o src/license.rs 72 | 73 | # Build in all the modes, with musl and glibc. 74 | for target in "" "--target=x86_64-unknown-linux-musl" 75 | do 76 | echo "target: $target" 77 | RUST_BACKTRACE=1 cargo test $target 78 | for profile in "" "--profile=dbgo" "-r" 79 | do 80 | echo "profile: $profile" 81 | cargo build $target $profile 82 | done 83 | done 84 | 85 | # Commit the version number bump. This is required by crates.io. We usually piggyback it to some useful commit. 86 | git status 87 | git commit -am "$COMMIT_MESSAGE" 88 | git push 89 | 90 | # Copy to my machines. 91 | 92 | #scp target/x86_64-unknown-linux-musl/dbgo/nnd dev:bin/new-nnd && scp target/x86_64-unknown-linux-musl/release/nnd dev:bin/new-nnd-release && ssh dev 'mv bin/new-nnd bin/nnd; mv bin/new-nnd-release bin/nnd-release;' 93 | 94 | cp target/x86_64-unknown-linux-musl/dbgo/nnd ../../nnd-release/nnd-new && mv ../../nnd-release/nnd-new ../../nnd-release/nnd && cp target/x86_64-unknown-linux-musl/release/nnd ../../nnd-release/nnd-release-new && mv ../../nnd-release/nnd-release-new ../../nnd-release/nnd-release 95 | cp target/dbgo/nnd ../../nnd-release/nnd-libc-new && mv ../../nnd-release/nnd-libc-new ../../nnd-release/nnd-libc && cp target/release/nnd ../../nnd-release/nnd-release-libc-new && mv ../../nnd-release/nnd-release-libc-new ../../nnd-release/nnd-release-libc 96 | 97 | # Push to itch. 98 | #butler push target/x86_64-unknown-linux-musl/release/nnd al13n/nnd:linux 99 | 100 | # Push to github. 101 | 102 | cp target/x86_64-unknown-linux-musl/dbgo/nnd nnd-dbgo 103 | trap 'rm nnd-dbgo' EXIT 104 | 105 | gh release create "$TAG" --notes "" target/x86_64-unknown-linux-musl/release/nnd nnd-dbgo 106 | 107 | rm nnd-dbgo 108 | trap - EXIT 109 | 110 | # Push to crates.io 111 | cargo publish 112 | 113 | echo "all done!" 114 | -------------------------------------------------------------------------------- /other_things/load_symbols.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | extern crate nnd; 3 | use nnd::{*, error::*, elf::*, log::*, symbols::*, types::*, arena::*, util::*}; 4 | use std::{fs::{File}, io::{self, Write}, sync::Arc, mem, sync::atomic::{AtomicBool, Ordering}, cell::UnsafeCell, thread, thread::JoinHandle}; 5 | 6 | fn main() -> Result<()> { 7 | unsafe {std::env::set_var("RUST_BACKTRACE", "1")}; 8 | let args: Vec = std::env::args().collect(); 9 | if args.len() < 2 { 10 | eprintln!("Usage: {} binary", args[0]); 11 | std::process::exit(1); 12 | } 13 | let path = args[1].clone(); 14 | 15 | dbg!(mem::size_of::()); 16 | dbg!(mem::size_of::()); 17 | dbg!(mem::size_of::()); 18 | dbg!(mem::size_of::()); 19 | dbg!(mem::size_of::()); 20 | dbg!(mem::size_of::()); 21 | dbg!(mem::size_of::()); 22 | dbg!(mem::size_of::()); 23 | 24 | let file = File::open(&path)?; 25 | let elf = Arc::new(ElfFile::from_file(path.clone(), &file, file.metadata()?.len())?); 26 | let symbols; 27 | 28 | { 29 | let _prof = ProfileScope::new("loading symbols".to_string()); 30 | 31 | let status = Arc::new(SymbolsLoadingStatus::new()); 32 | let num_threads = thread::available_parallelism().map_or(8, |n| n.get()) - 1; 33 | let loader = Arc::new(SyncUnsafeCell::new(SymbolsLoader::new(vec![elf], 1, num_threads, status.clone())?)); 34 | let num_shards = unsafe {&*loader.get()}.num_shards; 35 | let mut stage_idx = 1usize; 36 | while unsafe {&mut *loader.get()}.prepare_stage(stage_idx)? { 37 | eprintln!("stage {}", stage_idx); 38 | let threads: Vec> = (0..num_shards).map(|shard_idx| { 39 | let loader_copy = loader.clone(); 40 | let status_copy = status.clone(); 41 | thread::spawn(move || if let Err(e) = unsafe {&*loader_copy.get()}.run(stage_idx, shard_idx) { 42 | status_copy.cancel.store(true, Ordering::Relaxed); 43 | eprintln!("error: {}", e); 44 | }) 45 | }).collect(); 46 | for t in threads { 47 | t.join().unwrap(); 48 | } 49 | if status.cancel.load(Ordering::Relaxed) { 50 | return err!(Dwarf, "failed to load"); 51 | } 52 | stage_idx += 1; 53 | } 54 | symbols = Arc::into_inner(loader).unwrap().into_inner().into_result(); 55 | eprint!("\r"); 56 | } 57 | 58 | //dbg!(symbols.types.types.len()); 59 | //dbg!(symbols.types.name_to_type.len()); 60 | //dbg!(symbols.types.type_names.calculate_used_bytes()); 61 | //dbg!(symbols.types.misc_strings.calculate_used_bytes()); 62 | //dbg!(symbols.types.type_names.calculate_count()); 63 | //dbg!(symbols.types.misc_strings.calculate_count()); 64 | //dbg!(symbols.types.fields.len()); 65 | //dbg!(symbols.types.enumerands.len()); 66 | //dbg!(symbols.types.descriptor_to_type.len()); 67 | //dbg!(symbols.local_variables.len()); 68 | 69 | let mut out = io::BufWriter::new(io::stdout()); 70 | writeln!(out, "{} functions", symbols.functions.len())?; 71 | //for i in 0..symbols.types.types.len() { 72 | //writeln!(out, "{:?}", DumpType {types: &symbols.types, idx: TypeIdx(i)})?; 73 | //} 74 | 75 | /* 76 | for i in 0..symbols.local_variables.len() { 77 | let v = &symbols.local_variables[i]; 78 | writeln!(out, "{}: {} {:?}", i, unsafe {v.name()}, v)?; 79 | } 80 | for f in &symbols.addr_to_function { 81 | writeln!(out, "{:x}: {} {:?}", f.addr, unsafe {symbols.function_mangled_names.get_str_or(StringRef(f.mangled_name), "")}, f.local_variables)?; 82 | }*/ 83 | 84 | Ok(()) 85 | } 86 | -------------------------------------------------------------------------------- /src/range_index.rs: -------------------------------------------------------------------------------- 1 | use crate::{util::*}; 2 | use std::ops::Range; 3 | 4 | pub struct RangeIndexEntry { 5 | pub start: usize, 6 | pub end: usize, 7 | // Max value of `end` among this entry and all entries before it. 8 | pub max_end: usize, 9 | pub value: T, 10 | } 11 | 12 | pub struct RangeIndex { 13 | // Sorted by (start, value). 14 | pub ranges: Vec>, 15 | } 16 | 17 | impl RangeIndex { 18 | pub fn new(mut ranges: Vec>, name_for_logging: &str) -> Self { 19 | ranges.shrink_to_fit(); 20 | // Sort by value when start is equal. This makes inlined function work correctly automatically: the innermost function will be returned first. 21 | ranges.sort_unstable_by_key(|r| (r.start, r.value.clone())); 22 | let mut max_end = 0usize; 23 | let mut max_idx = 0usize; 24 | for i in 0..ranges.len() { 25 | if i - max_idx > 100000 { 26 | // We don't expect any address range (variable, function, or inlined function) to overlap a lot of other ranges. 27 | // If it does, this data structure gets very inefficient. If this turns out to happen in practice, we should switch 28 | // to a different data structure (and maybe we should switch anyway, this doesn't seem super efficient or compact even in good conditions). 29 | // This sanity check limits how slow we can get, but the index lookups will be missing entries (not just the bad entry 30 | // that we're skipping, some other entries too because we're reverting max_end to current range end rather than the second-max). 31 | eprintln!("warning: range {}-{} covers over {} other ranges (in {}). This is unexpectedly many, please investigate.", ranges[max_idx].start, ranges[max_idx].end, i - max_idx - 1, name_for_logging); 32 | max_end = ranges[i].end; 33 | max_idx = i; 34 | } else if ranges[i].end >= max_end { 35 | max_end = ranges[i].end; 36 | max_idx = i; 37 | } 38 | ranges[i].max_end = max_end; 39 | } 40 | 41 | Self {ranges: ranges} 42 | } 43 | 44 | // Index of the first entry with start > addr (or len() if there are none). 45 | // To get all entries containing addr, walk backwards: 46 | // while idx > 0 && ranges[idx-1].max_end > addr { 47 | // idx -= 1; 48 | // if ranges[idx].end > addr { 49 | // // ranges[idx] contains addr 50 | // } 51 | // } 52 | // Maybe we should make an iterator for this. 53 | pub fn upper_bound(&self, addr: usize) -> usize { 54 | self.ranges.partition_point(|r| r.start <= addr) 55 | } 56 | 57 | pub fn find(&self, addr: usize) -> Option<&RangeIndexEntry> { 58 | let mut idx = self.upper_bound(addr); 59 | while idx > 0 && self.ranges[idx-1].max_end > addr { 60 | idx -= 1; 61 | if self.ranges[idx].end > addr { 62 | return Some(&self.ranges[idx]); 63 | } 64 | } 65 | return None; 66 | } 67 | 68 | pub fn find_ranges(&self, sorted_addr_ranges: &Vec>) -> Vec<&RangeIndexEntry> { 69 | assert!(sorted_addr_ranges.windows(2).all(|a| a[0].end < a[1].start)); 70 | let mut res: Vec<&RangeIndexEntry> = Vec::new(); 71 | let mut idx = 0usize; 72 | let mut steps = 0usize; 73 | for range in sorted_addr_ranges { 74 | idx.set_max(self.ranges.partition_point(|r| r.max_end <= range.start)); 75 | while idx < self.ranges.len() && self.ranges[idx].start < range.end { 76 | steps += 1; 77 | if steps > 100000 { 78 | eprintln!("warning: lookup ranges overlap over {} index ranges", steps-1); 79 | return res; 80 | } 81 | 82 | if self.ranges[idx].end > range.start { 83 | res.push(&self.ranges[idx]); 84 | } 85 | idx += 1; 86 | } 87 | } 88 | res 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/pool.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 2 | pub struct Id { 3 | pub slot: usize, 4 | pub seqno: usize, 5 | } 6 | 7 | struct Slot { 8 | seqno: usize, 9 | val: Option, 10 | } 11 | 12 | pub struct Pool { 13 | slots: Vec>, 14 | vacant: Vec, 15 | next_seqno: usize, 16 | } 17 | 18 | impl Default for Id { 19 | fn default() -> Id { 20 | Id {slot: usize::MAX, seqno: usize::MAX} 21 | } 22 | } 23 | 24 | impl Pool { 25 | pub fn new() -> Self { 26 | Self {slots: Vec::new(), vacant: Vec::new(), next_seqno: 1} 27 | } 28 | 29 | pub fn add(&mut self, x: T) -> (Id, &mut T) { 30 | let seqno = self.next_seqno; 31 | self.next_seqno += 1; 32 | if let Some(slot) = self.vacant.pop() { 33 | let s = &mut self.slots[slot]; 34 | s.seqno = seqno; 35 | s.val = Some(x); 36 | (Id {slot, seqno}, s.val.as_mut().unwrap()) 37 | } else { 38 | self.slots.push(Slot {seqno, val: Some(x)}); 39 | (Id {slot: self.slots.len() - 1, seqno}, self.slots.last_mut().unwrap().val.as_mut().unwrap()) 40 | } 41 | } 42 | 43 | pub fn remove(&mut self, id: Id) -> Option { 44 | let s = match self.slots.get_mut(id.slot) { 45 | None => return None, 46 | Some(s) => s }; 47 | if s.seqno != id.seqno { 48 | return None; 49 | } 50 | let v = s.val.take(); 51 | if v.is_some() { 52 | self.vacant.push(id.slot); 53 | } 54 | v 55 | } 56 | 57 | pub fn clear(&mut self) { 58 | for slot in 0..self.slots.len() { 59 | if self.slots[slot].val.take().is_some() { 60 | self.vacant.push(slot); 61 | } 62 | } 63 | } 64 | 65 | pub fn try_get(&self, id: Id) -> Option<&T> { 66 | let s = match self.slots.get(id.slot) { 67 | None => return None, 68 | Some(s) => s }; 69 | if s.seqno != id.seqno { 70 | return None; 71 | } 72 | s.val.as_ref() 73 | } 74 | 75 | pub fn try_get_mut(&mut self, id: Id) -> Option<&mut T> { 76 | let s = match self.slots.get_mut(id.slot) { 77 | None => return None, 78 | Some(s) => s }; 79 | if s.seqno != id.seqno { 80 | return None; 81 | } 82 | s.val.as_mut() 83 | } 84 | 85 | pub fn get(&self, id: Id) -> &T { self.try_get(id).unwrap() } 86 | pub fn get_mut(&mut self, id: Id) -> &mut T { self.try_get_mut(id).unwrap() } 87 | 88 | pub fn iter(&self) -> PoolIter<'_, T> { 89 | PoolIter {pool: self, slot: 0} 90 | } 91 | 92 | pub fn iter_mut(&mut self) -> PoolIterMut<'_, T> { 93 | PoolIterMut {iter: self.slots.iter_mut(), idx: 0, slot: 0} 94 | } 95 | 96 | pub fn is_empty(&self) -> bool { 97 | assert!(self.slots.len() >= self.vacant.len()); 98 | self.slots.len() == self.vacant.len() 99 | } 100 | } 101 | 102 | pub struct PoolIter<'a, T> { 103 | pool: &'a Pool, 104 | slot: usize, 105 | } 106 | 107 | impl<'a, T> Iterator for PoolIter<'a, T> { 108 | type Item = (Id, &'a T); 109 | 110 | fn next(&mut self) -> Option { 111 | let mut slot = self.slot; 112 | while slot < self.pool.slots.len() && self.pool.slots[slot].val.is_none() { 113 | slot += 1; 114 | } 115 | self.slot = slot + 1; 116 | match self.pool.slots.get(slot) { 117 | None => None, 118 | Some(s) => Some((Id {slot, seqno: s.seqno}, s.val.as_ref().unwrap())) 119 | } 120 | } 121 | } 122 | 123 | pub struct PoolIterMut<'a, T> where T: 'a { 124 | iter: std::slice::IterMut<'a, Slot>, 125 | idx: usize, 126 | slot: usize, 127 | } 128 | 129 | impl<'a, T: 'a> Iterator for PoolIterMut<'a, T> { 130 | type Item = (Id, &'a mut T); 131 | 132 | fn next(&mut self) -> Option { 133 | loop { 134 | let idx = self.idx; 135 | self.idx += 1; 136 | match self.iter.next() { 137 | None => return None, 138 | Some(slot) => match &mut slot.val { 139 | None => (), 140 | Some(val) => return Some((Id {slot: idx, seqno: slot.seqno}, val)), 141 | } 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /other_things/pretty.txt: -------------------------------------------------------------------------------- 1 | _M_start: *T, _M_finish: *T, _M_end_of_storage: *T -> vec 2 | __begin_: *T, __end_: *T, __end_cap_: *T -> vec 3 | __data_: *char, __size_: u64 -> str, use name to rule out str 4 | _M_str: *char, _M_len: u64 -> str 5 | __data_: *T, __size_: u64 -> slice 6 | _M_ptr: *T, _M_extent: u64 -> slice 7 | __begin_: *T, __end_: *T -> slice 8 | _M_data: *T, _M_size: u64 -> slice 9 | c_start: *T, c_end: *T, c_end_of_storage: *T -> slice 10 | 11 | ptr: *T, cap: u64 -> RawVec 12 | buf: RawVec, len: u64 -> vec 13 | data_ptr: *T, length: u64 -> slice, use name to rule in str 14 | head: usize, len: usize, buf: RawVec -> VecDeque 15 | 16 | 17 | C++ 18 | remove common prefix and suffix (_M_*, __*_, __*, c_*) 19 | start|begin|data|ptr|str: *T 20 | (finish|end: *T)|(size|len|extent: u64) [, (end_of_storage|end_cap: *T)|(capacity|cap: u64)] -> slice [with capacity] 21 | ptr 22 | refcount|cntrl -> shared ptr 23 | TODO: unordered_{map,...}, {map,...}, optional, deque, tuple, list, forward_list 24 | TODO: absl::InlinedVector, absl::flat_hash_map, google::dense_hash_map, google::sparse_hash_map, Field, boost::intrusive::list, boost::intrusive::{set,multiset,map,multimap}, boost::container::flat_{set,map}, boost::container::small_vector, boost::shared_ptr 25 | Rust 26 | ptr: *T, cap: u64 -> slice // RawVec 27 | buf 28 | buf is RawVec 29 | len 30 | head -> VecDeque 31 | - -> slice with capacity, stringness from name (if "str" or "Str" in pre-template part of the name; use pre-unwrapping name!) 32 | data_ptr: *T 33 | length: u64 -> slice, stringness from name 34 | TODO: HashMap, HashSet 35 | 36 | 37 | 38 | 39 | 40 | absl::InlinedVector: 41 | {metadata_: usize, data_: union {allocated: {allocated_data: *T, allocated_capacity: *T}, inlined: {inlined_data: [i8; cap]}}} 42 | metadata_ & 1 - is_allocated 43 | metadata_ >> 1 - len 44 | 45 | 46 | Rust to do: 47 | Vec, String, OSString, PathBuf: {buf: {ptr, cap}, len} 48 | &[T], &str, Box<[T]>: {data_ptr, length} 49 | VecDeque: {head, len, buf: {ptr, cap}} 50 | 51 | Rust meh: 52 | BinaryHeap: {data: Vec} 53 | LinkedList: {head: Some({element, next: ..., prev: ...}), tail: Some(...), len} - traversable by hand 54 | BTreeMap, BTreeSet: looks traversable by hand 55 | RefCell: {borrow, value} 56 | Mutex, RWLock: {inner, poison, data} 57 | slice iter: {ptr, end_or_len} 58 | Rc, Arc, Weak: {ptr: {pointer: {strong, weak, value}}} 59 | 60 | 61 | C++ to do: 62 | vector: {_M_start, _M_finish, _M_end_of_storage} 63 | string_view: {_M_str, _M_len} 64 | span: {_M_ptr, _M_extent} 65 | valarray: {_M_size, _M_data} 66 | 67 | list: {_M_size, _M_next: {_M_next, _M_prev}, _M_prev: ...}, values after links 68 | forward_list: ... _M_next, values after links 69 | set, multiset, map, multimap: {_M_node_count, _M_header: {_M_parent: root, _M_left: min, _M_right: max}}, values after nodes 70 | unordered_{set,map,multiset,multimap}: {_M_element_count, _M_bucket_count, _M_buckets}, buckets are forward_list-like 71 | optional: {_M_payload, _M_engaged} 72 | tuple: chain of bases with multiple inheritance and _M_head_impl 73 | shared_ptr, weak_ptr: {_M_ptr, _M_refcount} 74 | 75 | C++ to figure out: 76 | string, wstring, u16string, u32string: {_M_string_length, _M_dataplus: {_M_p}, union {_M_local_buf, _M_allocated_capacity}} - ? 77 | deque, stack, queue: {_M_map, _M_map_size, _M_start, _M_finish} - ? 78 | 79 | C++ meh: 80 | priority_queue: vector 81 | any: ? 82 | 83 | 84 | libc++ to do: 85 | vector: {__begin_, __end_, __end_cap_} 86 | string_view, span: {__data_, __size_} 87 | valarray: {__begin_, __end_} 88 | 89 | list: {__size_alloc_, __end_: {__prev_, __next_}}, values after links 90 | forward_list: {__next_, 1: {__value_}} 91 | shared_ptr, weak_ptr: {__ptr_, __cntrl_} 92 | string, wstring, u16string, u32string: {__s: {0: {__is_long_, __size_}, __data_}, __l: {0: {__is_long_, __cap_}, __size_, __data_}, __r} 93 | optional: {union 0: {__null_state_, __val_}, __engaged_} 94 | variant: {__index, __data: {__dummy, __head: value, __tail: {__dummy, __head, __tail: {...}}}} 95 | 96 | libc++ to figure out: 97 | set, multiset, map, multimap: {__pair3__, __pair1_: {__left_, __right_, __parent_}} - ? 98 | unordered_{set,multiset,map,multimap}: {__bucket_list_: {__value_, __value_}, __p1_, __p2_, __p3_} - ? 99 | deque, stack, queue: {__size_, __start_, __map_: {__first_, __begin_, __end_, __end_cap_}} - ? 100 | 101 | libc++ meh: 102 | priority_queue: vector 103 | tuple: {__base_: {#base: {__value_}, #base: {__value_}, ...}} 104 | any: ? 105 | 106 | 107 | PODArrayBase: {c_start: *char, c_end, c_end_of_storage} 108 | PODArray: cast to *value_type 109 | -------------------------------------------------------------------------------- /testprogs/containers_zig.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const Shape = union(enum) { 4 | circle: f32, 5 | rectangle: struct { 6 | width: f32, 7 | height: f32, 8 | }, 9 | hyperrectangle: std.ArrayList(f32), 10 | }; 11 | 12 | fn shapeArea(shape: Shape) f32 { 13 | return switch (shape) { 14 | .circle => |radius| std.math.pi * radius * radius, 15 | .rectangle => |rect| rect.width * rect.height, 16 | .hyperrectangle => |sides| { 17 | var p: f32 = 1.0; 18 | for (sides.items) |s| { 19 | p *= s; 20 | } 21 | return p; 22 | } 23 | }; 24 | } 25 | 26 | fn optionalShapeArea(maybeShape: ?Shape) f32 { 27 | if (maybeShape) |shape| { 28 | return shapeArea(shape); 29 | } else { 30 | return 0.0; 31 | } 32 | } 33 | 34 | fn lessThan(context: void, a: u32, b: u32) std.math.Order { 35 | _ = context; 36 | return std.math.order(a, b); 37 | } 38 | 39 | pub fn main() !void { 40 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 41 | defer arena.deinit(); 42 | const allocator = arena.allocator(); 43 | 44 | var plain_array: [5]f32 = [_]f32{ 1.1, 2.5, 3.14, 4.2, 5.0 }; 45 | const plain_slice: []f32 = &plain_array; 46 | 47 | var list = std.ArrayList(f32).init(allocator); 48 | try list.appendSlice(plain_slice); 49 | 50 | // Optional and discriminated union. 51 | var shapes: [4]?Shape = undefined; 52 | shapes[0] = Shape{ .circle = 5.0 }; 53 | shapes[1] = Shape{ .rectangle = .{ .width = 4.0, .height = 6.0 } }; 54 | shapes[2] = Shape{ .hyperrectangle = list }; 55 | shapes[3] = null; 56 | 57 | var total_area: f32 = 0.0; 58 | for (shapes) |maybeShape| { 59 | total_area += optionalShapeArea(maybeShape); 60 | } 61 | 62 | // Some containers. 63 | var map = std.AutoHashMap(u64, u32).init(allocator); 64 | var string_map = std.StringHashMap(f64).init(allocator); 65 | var bounded_array = std.BoundedArray(i16, 210).init(0) catch unreachable; 66 | var array_map = std.ArrayHashMap(u64, bool, std.array_hash_map.AutoContext(u64), false).init(allocator); 67 | 68 | const Person = struct { 69 | name: []const u8, 70 | age: u8, 71 | }; 72 | var people = std.MultiArrayList(Person){}; 73 | 74 | const PriorityQueue = std.PriorityQueue(u32, void, lessThan); 75 | var queue = PriorityQueue.init(allocator, {}); 76 | 77 | var sll = std.SinglyLinkedList(u64){}; 78 | var buf_set = std.BufSet.init(allocator); 79 | var bit_set = std.bit_set.IntegerBitSet(512).initEmpty(); 80 | var seg_list = std.SegmentedList(usize, 16){}; 81 | 82 | for (0..200) |i| { 83 | try map.put(@intCast(i*10), @intCast(i*100)); 84 | 85 | const str_key = try std.fmt.allocPrint(allocator, "val{d}", .{i}); 86 | try string_map.put(str_key, @as(f64, @floatFromInt(i)) / 10.0); 87 | 88 | if (i < bounded_array.capacity()) { 89 | try bounded_array.append(@as(i16, @intCast(i)) - 100); 90 | } 91 | 92 | try array_map.put(@intCast(i * 10000), i % 2 == 0); 93 | 94 | const name = try std.fmt.allocPrint(allocator, "Person{d}", .{i}); 95 | try people.append(allocator, .{ .name = name, .age = @intCast(i % 100) }); 96 | 97 | try queue.add(@intCast(i * 5)); 98 | 99 | const node = try allocator.create(@TypeOf(sll).Node); 100 | node.* = .{ .data = i * 1000, .next = sll.first }; 101 | sll.first = node; 102 | 103 | const set_item = try std.fmt.allocPrint(allocator, "item{d}", .{i}); 104 | try buf_set.insert(set_item); 105 | 106 | if (i < bit_set.capacity() and i%2 == 0) { 107 | bit_set.set(i); 108 | } 109 | 110 | try seg_list.append(allocator, i * 10); 111 | } 112 | 113 | std.mem.doNotOptimizeAway( 114 | @as(u32, @intFromFloat(plain_array[0])) + 115 | @as(u32, @intFromFloat(plain_slice[0])) + 116 | map.get(40).? + 117 | @as(u32, @intFromFloat(string_map.get("val1").?)) + 118 | @as(u32, @as(u16, @bitCast(bounded_array.buffer[0]))) + 119 | @as(u32, @intFromBool(array_map.get(20000).?)) + 120 | @as(u32, people.items(.age)[0]) + 121 | queue.items[0] + 122 | @as(u32, @truncate(sll.first.?.data)) + 123 | @as(u32, @intFromBool(buf_set.contains("item1"))) + 124 | @as(u32, @intFromBool(bit_set.isSet(1))) + 125 | @as(u32, @intCast(seg_list.at(0).*)) + 126 | @as(u32, @intFromFloat(total_area)) 127 | ); 128 | 129 | @breakpoint(); 130 | } 131 | -------------------------------------------------------------------------------- /other_things/interp_sketch.rs: -------------------------------------------------------------------------------- 1 | struct InterpState { 2 | // 3 addressable memory ranges. 3 | stack: Vec, 4 | heap: Vec, 5 | debuggee_memory: MemReader, 6 | 7 | call_stack: Vec, 8 | 9 | error: Option, 10 | 11 | debuggee_memory_cache: [(/*addr*/ usize, Box<[u8; 4096]>)]; 12 | } 13 | 14 | impl Memory { 15 | fn read_usize<'a>(&'a mut self, addr: usize) -> usize { 16 | let address_space = addr >> 48; 17 | let addr = addr & 0xffffffffffff; 18 | let slice: &'a[u8] = match address_space { 19 | 0xdebe => { 20 | ...debuggee_memory_cache etc; 21 | } 22 | 0x57ac => { 23 | unsafe {std::slice::from_raw_parts(self.stack.as_ptr() as *const u8, self.stack.len() * 8)} 24 | } 25 | 0x4eab => { 26 | &self.heap 27 | } 28 | }; 29 | if addr + 8 > slice.len() { 30 | self.error = Some(error!(Runtime, "{} read out-of-bounds: {} + 8 > {}", if address_space == 0x57ac {"stack"} else {"heap"}, addr, slice.len())); 31 | return 0; 32 | } 33 | ...load_unaligned; 34 | } 35 | } 36 | 37 | // Inspired by https://github.com/maximecb/uvm/blob/main/vm/src/vm.rs 38 | 39 | #[repr(u8)] 40 | enum Instruction { 41 | Panic, 42 | Nop, 43 | 44 | PushU64, // ; push(imm); 45 | Pop, 46 | Dup, // v = stack[stack.len()-1]; push(v); 47 | Swap, 48 | GetN, // ; v = stack[stack.len()-1-offset]; push(v); 49 | SetN, // ; v = pop(); stack[stack.len()-1-offset] = v; 50 | 51 | LoadU8, // addr = pop(); v = memory.read_u8(addr); push(v); 52 | LoadU16, 53 | LoadU32, 54 | LoadU64, 55 | LoadI8, // sign-extended 56 | LoadI16, 57 | LoadI32, 58 | LoadF32, 59 | 60 | Store8, // addr = pop(); v = pop(); memory.write_u8(addr, v); 61 | Store16, 62 | Store32, 63 | Store64, 64 | 65 | SxI8, // sign-extend i8 -> i64 66 | SxI16, 67 | SxI32, 68 | TruncU8, 69 | TruncU16, 70 | TruncU32, 71 | 72 | IToF, // i64 -> f64 73 | UToF, // u64 -> f64 74 | FToI, 75 | 76 | Add, 77 | Sub, // y = pop(); x = pop(); push(x - y); 78 | Mul, 79 | UDiv, 80 | UMod, 81 | IDiv, 82 | IMod, 83 | FAdd, 84 | FSub, 85 | FMul, 86 | FDiv, 87 | FMod, 88 | And, 89 | Or, 90 | Xor, 91 | Not, 92 | LShift, 93 | RShift, 94 | IRShift, 95 | 96 | Eq, 97 | Ne, 98 | ULt, // y = pop(); x = pop(); v = if x < y {!0usize} else {0}; push(v); 99 | UGt, 100 | ULe, 101 | UGe, 102 | ILt, 103 | IGt, 104 | ILe, 105 | IGe, 106 | FLt, 107 | FGt, 108 | FLe, 109 | FGe, 110 | 111 | Jmp, // ; pc += offset 112 | Jz, // ; f = pop(); if f == 0 {pc += offset;} 113 | Jnz, 114 | 115 | Call, // function_idx: u32 116 | Ret, 117 | 118 | Register, // ; push(registers.get_int(idx)); 119 | Alloc, // ; ; push(address); 120 | Memcpy, // size = pop(); to = pop(); from = pop(); 121 | } 122 | 123 | 124 | 125 | #pretty("DB::PODArray") 126 | fn pretty_PODArray(a: $A) -> *[A::value_type] { 127 | use T = A::value_type; 128 | a.c_begin as *T .. a.c_end as *T 129 | } 130 | 131 | #pretty("::*std::*list", fields=["__end_"]) 132 | fn pretty_list_libcpp(list: $L) { 133 | use T = L::value_type; 134 | let end = &list.__end_; // if list is a copy, this is silently wrong! 135 | let elem = end.__next_; 136 | pretty_value("std::list of size {}", list.__size_alloc_); 137 | while elem != end { 138 | pretty_element((elem + 1) as *T); 139 | elem = elem.__next_; 140 | } 141 | } 142 | 143 | #pretty("::*std::*list", fields=["_M_next"]) 144 | fn pretty_list_libstdcpp(list: $L) { 145 | use T = L::value_type; 146 | pretty_value("std::list of size {}", list._M_size); 147 | let first = list._M_next; 148 | let elem = first; 149 | while elem->_M_next != first { 150 | pretty_element((elem + 1) as *T); 151 | elem = elem._M_next; 152 | } 153 | } 154 | 155 | watch expressions as auto-template functions; template over types of all identifiers and types; and over whether addresses are known; 156 | variables persisting across watch expressions; 157 | downcasting; where does auto-downcasting fit in?; 158 | passing big values to pretty printers - can be done without copying?; 159 | how to prevent taking address of local variables whose address is not known; 160 | prevent assignment to program variables and registers at compile time; 161 | dynamic arrays syntax and methods; 162 | strings; 163 | sorting?; 164 | yield (?); pagination?; 165 | pretty-printers for types introduced in the script itself; 166 | template structs; 167 | pretty-printers for synthetic types; 168 | ui to check which pretty-printer was applied, see template instantiation errors and runtime errors; 169 | printing, eprintln (shown in tooltip?) 170 | -------------------------------------------------------------------------------- /testprogs/simple_simd.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void cpuid(int leaf, int subleaf, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { 9 | __cpuid_count(leaf, subleaf, *eax, *ebx, *ecx, *edx); 10 | } 11 | 12 | static int has_avx512() { 13 | unsigned int eax, ebx, ecx, edx; 14 | cpuid(0, 0, &eax, &ebx, &ecx, &edx); 15 | if (eax < 7) return 0; 16 | cpuid(7, 0, &eax, &ebx, &ecx, &edx); 17 | return (ebx & (1 << 16)) != 0; 18 | } 19 | 20 | void fill_avx_registers() { 21 | uint32_t vals[16]; 22 | for (int i = 0; i < 16; i++) { 23 | vals[i] = (i + 1) * 0x01010101; 24 | } 25 | __asm__ volatile ( 26 | "vbroadcastss 0(%[vals]), %%ymm0\n\t" 27 | "vbroadcastss 4(%[vals]), %%ymm1\n\t" 28 | "vbroadcastss 8(%[vals]), %%ymm2\n\t" 29 | "vbroadcastss 12(%[vals]), %%ymm3\n\t" 30 | "vbroadcastss 16(%[vals]), %%ymm4\n\t" 31 | "vbroadcastss 20(%[vals]), %%ymm5\n\t" 32 | "vbroadcastss 24(%[vals]), %%ymm6\n\t" 33 | "vbroadcastss 28(%[vals]), %%ymm7\n\t" 34 | "vbroadcastss 32(%[vals]), %%ymm8\n\t" 35 | "vbroadcastss 36(%[vals]), %%ymm9\n\t" 36 | "vbroadcastss 40(%[vals]), %%ymm10\n\t" 37 | "vbroadcastss 44(%[vals]), %%ymm11\n\t" 38 | "vbroadcastss 48(%[vals]), %%ymm12\n\t" 39 | "vbroadcastss 52(%[vals]), %%ymm13\n\t" 40 | "vbroadcastss 56(%[vals]), %%ymm14\n\t" 41 | "vbroadcastss 60(%[vals]), %%ymm15\n\t" 42 | : 43 | : [vals] "r"(vals) 44 | : "memory" 45 | ); 46 | } 47 | 48 | __attribute__((target("avx512f"))) 49 | void fill_avx512_registers() { 50 | uint32_t vals[32]; 51 | uint8_t kmasks[8]; 52 | 53 | for (int i = 0; i < 32; i++) { 54 | vals[i] = (i + 1) * 0x01010101; 55 | } 56 | for (int i = 0; i < 8; i++) { 57 | kmasks[i] = (1U << i); 58 | } 59 | 60 | __asm__ volatile ( 61 | "vbroadcastss 0(%[vals]), %%zmm0\n\t" 62 | "vbroadcastss 4(%[vals]), %%zmm1\n\t" 63 | "vbroadcastss 8(%[vals]), %%zmm2\n\t" 64 | "vbroadcastss 12(%[vals]), %%zmm3\n\t" 65 | "vbroadcastss 16(%[vals]), %%zmm4\n\t" 66 | "vbroadcastss 20(%[vals]), %%zmm5\n\t" 67 | "vbroadcastss 24(%[vals]), %%zmm6\n\t" 68 | "vbroadcastss 28(%[vals]), %%zmm7\n\t" 69 | "vbroadcastss 32(%[vals]), %%zmm8\n\t" 70 | "vbroadcastss 36(%[vals]), %%zmm9\n\t" 71 | "vbroadcastss 40(%[vals]), %%zmm10\n\t" 72 | "vbroadcastss 44(%[vals]), %%zmm11\n\t" 73 | "vbroadcastss 48(%[vals]), %%zmm12\n\t" 74 | "vbroadcastss 52(%[vals]), %%zmm13\n\t" 75 | "vbroadcastss 56(%[vals]), %%zmm14\n\t" 76 | "vbroadcastss 60(%[vals]), %%zmm15\n\t" 77 | "vbroadcastss 64(%[vals]), %%zmm16\n\t" 78 | "vbroadcastss 68(%[vals]), %%zmm17\n\t" 79 | "vbroadcastss 72(%[vals]), %%zmm18\n\t" 80 | "vbroadcastss 76(%[vals]), %%zmm19\n\t" 81 | "vbroadcastss 80(%[vals]), %%zmm20\n\t" 82 | "vbroadcastss 84(%[vals]), %%zmm21\n\t" 83 | "vbroadcastss 88(%[vals]), %%zmm22\n\t" 84 | "vbroadcastss 92(%[vals]), %%zmm23\n\t" 85 | "vbroadcastss 96(%[vals]), %%zmm24\n\t" 86 | "vbroadcastss 100(%[vals]), %%zmm25\n\t" 87 | "vbroadcastss 104(%[vals]), %%zmm26\n\t" 88 | "vbroadcastss 108(%[vals]), %%zmm27\n\t" 89 | "vbroadcastss 112(%[vals]), %%zmm28\n\t" 90 | "vbroadcastss 116(%[vals]), %%zmm29\n\t" 91 | "vbroadcastss 120(%[vals]), %%zmm30\n\t" 92 | "vbroadcastss 124(%[vals]), %%zmm31\n\t" 93 | 94 | "kmovb 0(%[kmasks]), %%k0\n\t" 95 | "kmovb 1(%[kmasks]), %%k1\n\t" 96 | "kmovb 2(%[kmasks]), %%k2\n\t" 97 | "kmovb 3(%[kmasks]), %%k3\n\t" 98 | "kmovb 4(%[kmasks]), %%k4\n\t" 99 | "kmovb 5(%[kmasks]), %%k5\n\t" 100 | "kmovb 6(%[kmasks]), %%k6\n\t" 101 | "kmovb 7(%[kmasks]), %%k7\n\t" 102 | : 103 | : [vals] "r"(vals), [kmasks] "r"(kmasks) 104 | : "memory" 105 | ); 106 | } 107 | 108 | void add_some_numbers_using_avx() { 109 | int data[32]; 110 | srand(time(NULL)); 111 | for (int i = 0; i < 32; i++) { 112 | data[i] = rand() % 100; 113 | } 114 | 115 | __m256i accum = _mm256_setzero_si256(); 116 | 117 | for (int i = 0; i < 32; i += 8) { 118 | __m256i vec = _mm256_loadu_si256((__m256i*)&data[i]); 119 | accum = _mm256_add_epi32(accum, vec); 120 | } 121 | 122 | int temp[8]; 123 | _mm256_storeu_si256((__m256i*)temp, accum); 124 | 125 | int sum = 0; 126 | for (int i = 0; i < 8; i++) { 127 | sum += temp[i]; 128 | } 129 | printf("sum: %d\n", sum); 130 | } 131 | 132 | int main() { 133 | fill_avx_registers(); 134 | if (has_avx512()) { 135 | fill_avx512_registers(); 136 | } 137 | add_some_numbers_using_avx(); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /other_things/singlestep_vs_group_stop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | #define CHECK(x) if (!(x)) { cerr << "check failed on line " << __LINE__ << ": " << #x << endl; exit(1); } 17 | #define CHECK_EQ(a, b) { auto hyg_a = (a); auto hyg_b = (b); if (hyg_a != hyg_b) { cerr << "check failed on line " << __LINE__ << ": " #a " == " #b " (" << hyg_a << " != " << hyg_b << ")" << endl; exit(1); } } 18 | #define ERRNO(x) if ((x) < 0) { perror(#x); exit(1); } 19 | 20 | // This reproduces a somewhat unexpected behavior of PTRACE_SINGLESTEP. Scenario: 21 | // 1. PTRACE_SINGLESTEP for a thread that's blocked on syscall. 22 | // 2. The thread gets stopped because of some signal (unrelated to the SINGLESTEP). 23 | // 3. wait() reports a SIGTRAP instead of the signal that stopped the thread - a little unexpected, but 24 | // understandable (the signal delivery interrupted the syscall, which can be considered a step). 25 | // 4. PTRACE_CONT. 26 | // 5. The thread immediately stops again and reports the signal. 27 | // 28 | // A similar but more weird behavior that I saw in practice: 29 | // 1. PTRACE_SINGLESTEP for a thread that's blocked on syscall. 30 | // 2. The thread gets stopped by a group-stop. 31 | // 3. wait() reports the group-stop. 32 | // 4. PTRACE_CONT. 33 | // 5. wait() reports SIGTRAP. This seems unexpected and difficult to handle correctly! 34 | // I guess what happens is that both the group-stop event and SIGTRAP event get enqueued for delivery at once, 35 | // then get delivered in arbitrary order, so we may get either this scenario or the scenario above arbitrarily. 36 | 37 | void __attribute__((noinline)) child_run() { 38 | struct timespec req, rem; 39 | req.tv_sec = 5; 40 | req.tv_nsec = 0; 41 | int pid = syscall(SYS_getpid); 42 | 43 | syscall(SYS_tgkill, pid, pid, SIGSTOP); 44 | syscall(SYS_nanosleep, &req, &rem); 45 | syscall(SYS_exit, 42); 46 | } 47 | 48 | int main(int argc, char**argv) { 49 | if (argc == 2) { 50 | cout<<"running child function only"<>16, 0); 71 | 72 | cout<<"continuing"<>16, 0); 83 | ERRNO(ptrace(PTRACE_GETREGS, pid, 0, ®s)); 84 | cout<<"stopped at 0x"<>16, 0); 97 | ERRNO(ptrace(PTRACE_GETREGS, pid, 0, ®s)); 98 | cout<<"stopped at 0x"<>16, 0); CHECK_EQ(WSTOPSIG(w), SIGSTOP); 105 | } else { 106 | CHECK_EQ(WSTOPSIG(w), SIGSTOP); 107 | } 108 | 109 | cout<<"sleeping"<>16, 0); 119 | 120 | cout<<"done"< = vec![1, 2, 3]; // yes 22 | let mut empty_v: Vec = Vec::new(); 23 | let bh: BinaryHeap = BinaryHeap::from(vec![1, 2, 3]); // good enough 24 | 25 | let vd: VecDeque = VecDeque::from(vec![1, 2, 3]); // yes 26 | 27 | let string = String::from("hello"); // yes 28 | let str_slice: &str = "world"; // yes 29 | let cstring = CString::new("hello").unwrap(); // yes 30 | let osstring = OsString::from("hello"); // yes 31 | let pathbuf = PathBuf::from("/path/to/file"); // yes 32 | 33 | let ll: LinkedList = LinkedList::from_iter(vec![1, 2, 3]); // no 34 | 35 | let bm: BTreeMap = BTreeMap::from([(1, "one"), (2, "two")]); // no 36 | let bs: BTreeSet = BTreeSet::from_iter(vec![1, 2, 3]); // no 37 | 38 | let mut hm: HashMap = HashMap::new(); // yes 39 | for i in 1..200 { 40 | let x = i*1310%10000; 41 | hm.insert(x, format!("v{}", x)); 42 | } 43 | let hs: HashSet = HashSet::from_iter(vec![1, 2, 3]); // yes 44 | 45 | let rc: Rc = Rc::new(42); // no 46 | let arc: Arc = Arc::new(42); // no 47 | let weak: Weak = Arc::downgrade(&arc); // no 48 | 49 | // Complicated and not worth it. 50 | let fn_ptr: fn(i32) -> i32 = |x| x + 1; 51 | let closure = |x: i32| x + 1; 52 | 53 | // No designated pretty-printers needed below. 54 | 55 | let b: Box = Box::new(42); 56 | let raw_ptr: *const i32 = &42; 57 | let raw_mut_ptr: *mut i32 = &mut 42; 58 | let cell: Cell = Cell::new(42); 59 | let atomic_bool = AtomicBool::new(true); 60 | let atomic_i32 = AtomicI32::new(42); 61 | let atomic_u64 = AtomicU64::new(42); 62 | let pin = Pin::new(&42); 63 | let phantom: PhantomData = PhantomData; 64 | let non_zero = NonZeroU8::new(42).unwrap(); 65 | 66 | let refcell: RefCell = RefCell::new(42); 67 | let mutex: Mutex = Mutex::new(42); 68 | let rwlock: RwLock = RwLock::new(42); 69 | 70 | let some: Option = Some(42); 71 | let none: Option = None; 72 | let ok: Result = Ok(42); 73 | let err: Result = Err("error"); 74 | 75 | // Iterators 76 | black_box(&v); 77 | let iter = v.iter(); 78 | let _ = black_box(iter); 79 | let into_iter = v.into_iter(); 80 | black_box(&empty_v); 81 | let iter_mut = empty_v.iter_mut(); 82 | 83 | let range = 0..10; 84 | let range_inclusive = 0..=10; 85 | let range_to = ..10; 86 | let range_from = 10..; 87 | 88 | // Moo 89 | let cow1: Cow = Cow::Borrowed("hello"); 90 | let cow2: Cow = Cow::Owned("world".to_string()); 91 | 92 | let duration = Duration::from_secs(42); 93 | let instant = Instant::now(); 94 | let system_time = SystemTime::now(); 95 | 96 | black_box(vd); 97 | black_box(ll); 98 | black_box(hm); 99 | black_box(bm); 100 | black_box(hs); 101 | black_box(bs); 102 | black_box(bh); 103 | black_box(b); 104 | black_box(rc); 105 | black_box(arc); 106 | black_box(weak); 107 | black_box(cell); 108 | black_box(refcell); 109 | black_box(mutex); 110 | black_box(rwlock); 111 | black_box(atomic_bool); 112 | black_box(atomic_i32); 113 | black_box(atomic_u64); 114 | black_box(some); 115 | black_box(none); 116 | let _ = black_box(ok); 117 | let _ = black_box(err); 118 | black_box(string); 119 | black_box(str_slice); 120 | black_box(cstring); 121 | black_box(osstring); 122 | black_box(pathbuf); 123 | let _ = black_box(into_iter); 124 | let _ = black_box(iter_mut); 125 | black_box(range); 126 | black_box(range_inclusive); 127 | black_box(range_to); 128 | black_box(range_from); 129 | black_box(slice); 130 | black_box(mut_slice); 131 | black_box(cow1); 132 | black_box(cow2); 133 | black_box(pin); 134 | black_box(phantom); 135 | black_box(non_zero); 136 | black_box(duration); 137 | black_box(instant); 138 | black_box(system_time); 139 | black_box(fn_ptr); 140 | black_box(&closure); 141 | black_box(raw_ptr); 142 | black_box(raw_mut_ptr); 143 | } 144 | -------------------------------------------------------------------------------- /other_things/tui_sketch.rs: -------------------------------------------------------------------------------- 1 | figure out input; 2 | focus path, assembled from pieces; 3 | use the focus mechanism for table selection?; 4 | do multiple renders if keys dispatch to different boxes, up to some time limit (have timestamp with each key, discard if too old); 5 | think more about context menus, main menu, mouse, gui; 6 | 7 | // Immediate-mode UI library, inspired by https://www.rfleury.com/p/ui-series-table-of-contents 8 | 9 | struct Rect { 10 | x: usize, 11 | y: usize, 12 | w: usize, 13 | h: usize, 14 | } 15 | 16 | bitflags! { pub struct WidgetFlags : u32 { 17 | WIDTH_FROM_CONTENT, 18 | HEIGHT_FROM_CONTENT, 19 | FLOATING_X, 20 | FLOATING_Y, 21 | 22 | SCROLL_X = 0x8, 23 | SCROLL_Y = 0x10, 24 | SCROLLBAR_Y = 0x20, // even when the scrollbar is not visible, one column of width is reserved for it 25 | SCROLL_HERE, // the nearest ancestor scrollable area should scroll such that this widget is visible 26 | 27 | LINE_WRAP, 28 | ELLIPSIS_FOR_TRUNCATED_TEXT, 29 | HORIZONTAL_CLIPPING_ARROWS, // if the line of text is clipped by parent scroll area, show arrows at the clipped end(s) 30 | REPEAT_TEXT_VERTICALLY, 31 | REPEAT_TEXT_HORIZONTALLY, 32 | FILL_BACKGROUND, 33 | }} 34 | 35 | struct Widget { 36 | // State that may be used across frames. 37 | 38 | identity: usize, // identifier for recognizing the widget across frames; 0 means unset and not preserved across frames 39 | rect: Rect, 40 | flags: WidgetFlags, 41 | 42 | parent: WidgetIdx, 43 | first_child: WidgetIdx, 44 | last_child: WidgetIdx, 45 | prev_sibling: WidgetIdx, 46 | next_sibling: WidgetIdx, 47 | 48 | // State only used for current frame. 49 | 50 | background: Option, 51 | text: Range, // range of lines in all_text 52 | line_wrap_limit: usize, // max height when LINE_WRAP is enabled 53 | } 54 | impl Default for Widget; 55 | struct WidgetIdx(usize); 56 | 57 | struct UI { 58 | // State preserved across frames. 59 | prev_widgets: Vec, // sorted by identity 60 | 61 | all_text: StyledText, 62 | widgets: Vec, 63 | parent_stack: Vec, 64 | 65 | } 66 | impl Frame { 67 | fn add(&mut self, w: Widget); 68 | } 69 | 70 | 71 | hints 72 | panel has fixed height 73 | horizontal scrolling 74 | always 2 columns 75 | two text areas with widths determined by content; 76 | status 77 | panel has fixed height 78 | state line is a full-width widget with background color 79 | other info is just a text area; 80 | tabs 81 | scrollable x, stack x 82 | scroll indicator arrows 83 | auto scroll to selected tab 84 | each tab header has width determined by content 85 | separators; 86 | watches 87 | table header 88 | name and value columns, widths as percentage of parent 89 | row has two flags: expanded, line-wrapped 90 | if has children, right arrow goes collapsed -> expanded -> expanded+wrapped; left arrow always goes to collapsed+non-wrapped 91 | line-wrapped rows wrap to at most ~20 lines, in each column separately 92 | non-wrapped rows truncate text with ellipsis 93 | arrow and indentation are their own widgets, so that name column line-wraps correctly, and to make the arrow clickable in future 94 | non-wrapped lines grouped into placeholders when offscreen 95 | wrapped lines always emitted individually and height recalculated every frame even if offscreen 96 | scrollable y, scrollbar 97 | auto-scroll to selected line 98 | when editing a watch, temporarily disable line wrapping for its row; 99 | disassembly 100 | horizontally scrollable area, inside it the header row and the main vertically scrollable area 101 | scrollable y, scrollbar 102 | each visible line is a text area, sized by content, with arrow truncation indicators (HORIZONTAL_CLIPPING_ARROWS) 103 | placeholders for offscreen lines, use longest line width as width 104 | tabs 105 | header line; 106 | code 107 | same as disassembly: tabs, header line, text area per line, etc; 108 | binaries, breakpoints, stack, threads 109 | table widget 110 | h scroll area, inside it are table header and v scrollable area 111 | no placeholders 112 | all columns have content-based width, long column is last 113 | optional min width for each column (e.g. for address and idx to avoid jumping around too much) 114 | binaries: optional second line for error message, maybe with merged rows 115 | binaries: progress bar, merge cells, use parent width (don't bother excluding it from horizontal scrolling; no special treatment in Table) 116 | stack: truncation message is just a row 117 | windowed mode: early layout, placeholders; 118 | threads 119 | some min column widths manually calculated based on full list of threads 120 | filter: box, text input, affects table contents 121 | sorting by any column, highlight it in the header if not default; 122 | windows 123 | border lines are widgets with repeated text 124 | just keep layout.rs for now; 125 | search dialog 126 | separate tree 127 | borders and title 128 | progress bar 129 | windowed table with one column and no header; 130 | maximizing a panel: move to separate tree; 131 | 132 | struct Table; 133 | impl Table { 134 | fn new(frame: &mut Frame) -> Self; 135 | fn enable_selected_line_indicator(&mut self); 136 | fn header_cell(&mut self, text: usize, min_width: usize) -> WidgetIdx; 137 | fn finish_header(&mut self); 138 | fn hide_header(&mut self); 139 | fn enable_windowed_mode(&mut self, num_rows: usize, selected_row: usize) -> Range; 140 | fn cell(&mut self, text: usize) -> WidgetIdx; 141 | fn merged_cell(&mut self, text: usize, count: usize) -> WidgetIdx; 142 | fn finish_subrow(&mut self); 143 | fn select_current_row(&mut self); 144 | fn finish_row(&mut self); 145 | fn finish(&mut self, &mut Frame); 146 | } 147 | -------------------------------------------------------------------------------- /testprogs/containers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | int main() { 33 | // Comments say whether pretty-printers work for [libstdc++, libc++]. 34 | 35 | // Slice-like. 36 | std::vector vec = {1, 2, 3}; // [yes, yes] 37 | std::string_view strview = "hello"; // [yes, yes] 38 | std::valarray va = {1, 2, 3}; // [yes, yes] 39 | std::span sp(vec); // [yes, yes] 40 | 41 | // Smart pointers. 42 | std::shared_ptr sptr = std::make_shared(42); // [yes, yes] 43 | std::weak_ptr wptr = sptr; // [yes, yes] 44 | 45 | // String. 46 | std::string short_str = "hello"; // [yes, yes] 47 | std::string long_str(100, '.'); 48 | 49 | // Wide strings. 50 | std::wstring wstr = L"hello"; // [good enough, good enough] 51 | std::u16string u16str = u"hello"; // [good enough, good enough] 52 | std::u32string u32str = U"hello"; // [good enough, good enough] 53 | 54 | auto pv = std::make_shared>(std::vector({10, 20, 30})); 55 | 56 | // Deque. 57 | std::deque deq = {1, 2, 3}; // [yes, yes] 58 | std::stack stk; // [yes, yes] 59 | stk.push(1); 60 | std::queue que; // [yes, yes] 61 | que.push(1); 62 | for (int i = 0; i < 100; ++i) { 63 | deq.push_back(i * 10); 64 | stk.push(i * 100); 65 | que.push(i * 1000); 66 | } 67 | for (int i = 0; i < 50; ++i) { 68 | deq.pop_front(); 69 | stk.pop(); 70 | que.pop(); 71 | } 72 | 73 | // Linked lists. 74 | std::list lst = {1, 2, 3}; // [yes, yes] 75 | std::forward_list flst = {1, 2, 3}; // [yes, yes] 76 | 77 | // Trees. 78 | std::set set = {1, 2, 3}; // [yes, yes] 79 | std::multiset mset = {1, 1, 2, 3}; // [yes, yes] 80 | std::map map = {{1, "one"}, {2, "two"}}; // [yes, yes] 81 | std::multimap mmap = {{1, "one"}, {1, "uno"}, {2, "two"}}; // [yes, yes] 82 | 83 | // Hash tables. 84 | std::unordered_set uset = {1, 2, 3}; // [yes, yes] 85 | std::unordered_multiset umset = {1, 1, 2, 3}; // [yes, yes] 86 | std::unordered_map umap = {{1, "one"}, {2, "two"}}; // [yes, yes] 87 | std::unordered_multimap ummap = {{1, "one"}, {1, "uno"}, {2, "two"}}; // [yes, yes] 88 | 89 | std::unique_ptr> pvec(new std::vector{10, 20, 30, 40}); 90 | 91 | // Discriminated unions. 92 | std::optional opt = 42; // [yes, yes] 93 | std::optional opt_str = "henlo"; 94 | std::optional>> opt_nested = std::make_optional(std::make_optional(std::vector {10, 20, 30})); 95 | std::optional>> opt_half_null; 96 | opt_half_null.emplace(); 97 | std::optional opt_null; 98 | std::variant var = 3.14; // [no, no] 99 | 100 | // Complicated and not worth it. 101 | std::any any_val = 42; // [no, no] 102 | std::function func = [](int x) { return x * 2; }; // [no, no] 103 | std::future fut = std::async(std::launch::deferred, []() { return 42; }); // [no, no] 104 | std::promise prom; // [no, no] 105 | std::exception_ptr eptr = std::make_exception_ptr(std::runtime_error("test")); // [no, no] 106 | std::error_code ec = std::make_error_code(std::errc::invalid_argument); // [no, no] 107 | 108 | // Things that are ok without designated pretty printers. 109 | std::array arr = {1, 2, 3}; 110 | int raw_array[5] = {1, 2, 3, 4, 5}; 111 | std::priority_queue pque; // not sorted, but meh 112 | pque.push(1); 113 | std::unique_ptr uptr = std::make_unique(42); 114 | std::pair pair = {1, 2.0}; 115 | std::tuple tuple = {1, 2.0, 'a'}; 116 | auto now = std::chrono::system_clock::now(); 117 | auto duration = std::chrono::hours(1); 118 | std::thread t([](){}); 119 | std::mutex mtx; 120 | std::condition_variable cv; 121 | std::bitset<8> bits(42); 122 | std::complex c(1.0, 2.0); 123 | auto range = std::views::iota(1, 10); 124 | 125 | // Prevent optimizations 126 | volatile int64_t dummy = vec[0] + arr[0] + arr[2] + *uptr + *sptr + short_str[0] + long_str[0] + pair.first + std::get<0>(tuple) + 127 | (int64_t)&opt + (int64_t)&opt_str + (int64_t)&opt_nested + (int64_t)&opt_half_null + (int64_t)&opt_null + 128 | (int)std::get(var) + std::any_cast(any_val) + func(1) + 129 | bits.count() + c.real() + va[0] + sp[0] + *range.begin() + strview[0] + 130 | wstr[0] + u16str[0] + u32str[0] + (*pv)[0]; 131 | (void)dummy; 132 | 133 | t.join(); 134 | 135 | 136 | // Make everything big. 137 | std::vector>> mat; 138 | for (int i = 0; i < 1000000; ++i) { 139 | vec.push_back(i * 10); 140 | long_str.push_back((char)('a'+__builtin_popcount(i))); 141 | deq.push_back(i * 11); 142 | stk.push(i * 12); 143 | lst.push_back(i * 13); 144 | flst.push_front(i * 14); 145 | set.insert(i * 15); 146 | mset.insert(i / 10); 147 | map.emplace(i * 16, std::to_string(i * 160)); 148 | mmap.emplace(i / 5, std::to_string(i / 5 * 10)); 149 | uset.insert(i * 17); 150 | umset.insert(i / 6); 151 | umap.emplace(i * 18, std::to_string(i * 180)); 152 | ummap.emplace(i / 4, std::to_string(i / 4 * 10)); 153 | 154 | int row = i / 1000; 155 | mat.resize(row + 1); 156 | mat[row].emplace_back((double)i, (double)(i/10)); 157 | } 158 | auto big_array = std::make_unique>(); 159 | 160 | volatile int dummy2 = vec[0] + arr[0] + *uptr + *sptr + short_str[0] + long_str[0] + pair.first + std::get<0>(tuple) + 161 | (int)std::get(var) + std::any_cast(any_val) + func(1) + 162 | bits.count() + c.real() + va[0] + sp[0] + *range.begin() + strview[0] + 163 | wstr[0] + u16str[0] + u32str[0] + (*pv)[0] + mat.size() + mat[10].size() + pvec->size(); 164 | (void)dummy2; 165 | 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, io}; 2 | 3 | #[derive(Clone, Copy, Debug)] 4 | pub enum ErrorCode { 5 | MalformedExecutable = 1, 6 | UnsupportedExecutable = 2, 7 | Internal = 3, 8 | Usage = 4, 9 | Cancelled = 5, 10 | Format = 6, 11 | Dwarf = 7, 12 | ProcessState = 9, 13 | NotImplemented = 10, 14 | NoSection = 12, 15 | Loading = 13, 16 | OptimizedAway = 14, 17 | MissingSymbols = 15, 18 | Sanity = 16, 19 | OutOfHardwareBreakpoints = 17, 20 | NoCodeLocations = 18, 21 | TooLong = 20, 22 | Syntax = 21, 23 | NoVariable = 22, 24 | TypeMismatch = 23, 25 | Runtime = 24, 26 | NoFunction = 25, 27 | Environment = 26, 28 | NotCalculated = 27, 29 | NoField = 28, 30 | NotContainer = 29, 31 | Network = 30, 32 | Disabled = 31, 33 | } 34 | 35 | #[derive(Debug)] 36 | pub enum ErrorEnum { 37 | IO(io::Error), 38 | Code(ErrorCode), 39 | } 40 | 41 | #[derive(Clone)] 42 | pub struct Error { 43 | pub error: ErrorEnum, 44 | // TODO: Make it enum between &'static str and String, to make simple errors fast. 45 | pub message: String, 46 | } 47 | 48 | pub type Result = std::result::Result; 49 | 50 | pub trait ResultWithClonableError { fn as_ref_clone_error(&self) -> Result<&T>; } 51 | impl ResultWithClonableError for Result { fn as_ref_clone_error(&self) -> Result<&T> { match self { Ok(t) => Ok(t), Err(e) => Err(e.clone()) } } } 52 | 53 | impl Error { 54 | pub fn new(code: ErrorCode, message: String) -> Error { 55 | Error {error: ErrorEnum::Code(code), message} 56 | } 57 | 58 | pub fn from_io_error(e: io::Error, message: String) -> Error { 59 | Error {error: ErrorEnum::IO(e), message} 60 | } 61 | 62 | pub fn is_no_section(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::NoSection) => true, _ => false, } } 63 | pub fn is_loading(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::Loading) => true, _ => false, } } 64 | pub fn is_usage(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::Usage) => true, _ => false, } } 65 | pub fn is_missing_symbols(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::MissingSymbols) => true, _ => false, } } 66 | pub fn is_out_of_hardware_breakpoints(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::OutOfHardwareBreakpoints) => true, _ => false, } } 67 | pub fn is_too_long(&self) -> bool { match self.error { ErrorEnum::Code(ErrorCode::TooLong) => true, _ => false, } } 68 | pub fn is_io_not_found(&self) -> bool { match &self.error { ErrorEnum::IO(e) if e.kind() == io::ErrorKind::NotFound => true, _ => false, } } 69 | pub fn is_io_permission_denied(&self) -> bool { match &self.error { ErrorEnum::IO(e) if e.kind() == io::ErrorKind::PermissionDenied => true, _ => false, } } 70 | pub fn is_io_invalid_input(&self) -> bool { match &self.error { ErrorEnum::IO(e) if e.kind() == io::ErrorKind::InvalidInput => true, _ => false, } } 71 | pub fn is_not_calculated(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NotCalculated) => true, _ => false, } } 72 | pub fn is_no_field(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NoField) => true, _ => false, } } 73 | pub fn is_no_function(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NoFunction) => true, _ => false, } } 74 | pub fn is_no_variable(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NoVariable) => true, _ => false, } } 75 | pub fn is_not_container(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NotContainer) => true, _ => false, } } 76 | pub fn is_type_mismatch(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::TypeMismatch) => true, _ => false, } } 77 | pub fn is_not_implemented(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::NotImplemented) => true, _ => false, } } 78 | pub fn is_disabled(&self) -> bool { match &self.error { ErrorEnum::Code(ErrorCode::Disabled) => true, _ => false, } } 79 | } 80 | 81 | impl From for Error { 82 | fn from(error: io::Error) -> Self { 83 | Error {error: ErrorEnum::IO(error), message: String::new()} 84 | } 85 | } 86 | 87 | impl From for Error { 88 | fn from(error: std::num::ParseIntError) -> Self { 89 | Error {error: ErrorEnum::Code(ErrorCode::Format), message: format!("{}", error)} 90 | } 91 | } 92 | 93 | impl From for Error { 94 | fn from(error: gimli::Error) -> Self { 95 | Error {error: ErrorEnum::Code(ErrorCode::Dwarf), message: format!("{}", error)} 96 | } 97 | } 98 | 99 | impl From for Error { 100 | fn from(error: std::str::Utf8Error) -> Self { 101 | Error {error: ErrorEnum::Code(ErrorCode::Format), message: format!("{}", error)} 102 | } 103 | } 104 | 105 | impl From for Error { 106 | fn from(error: std::string::FromUtf8Error) -> Self { 107 | Error {error: ErrorEnum::Code(ErrorCode::Format), message: format!("{}", error)} 108 | } 109 | } 110 | 111 | impl From for Error { 112 | fn from(error: std::fmt::Error) -> Self { 113 | Error {error: ErrorEnum::Code(ErrorCode::Format), message: format!("{}", error)} 114 | } 115 | } 116 | 117 | impl From for Error { 118 | fn from(error: std::env::VarError) -> Self { 119 | Error {error: ErrorEnum::Code(ErrorCode::Environment), message: format!("{}", error)} 120 | } 121 | } 122 | 123 | // For printing to log. 124 | impl fmt::Debug for Error { 125 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 126 | match &self.error { 127 | &ErrorEnum::Code(code) => write!(f, "{}: {}", code as i64, self.message), 128 | ErrorEnum::IO(error) => write!(f, "{}: {}", self.message, error), 129 | } 130 | } 131 | } 132 | 133 | // For showing to the user. 134 | impl fmt::Display for Error { 135 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 136 | match &self.error { 137 | &ErrorEnum::Code(_) => write!(f, "{}", self.message), 138 | ErrorEnum::IO(error) if self.message.is_empty() => write!(f, "{}", error), 139 | ErrorEnum::IO(error) => write!(f, "{}: {}", self.message, error), 140 | } 141 | } 142 | } 143 | 144 | impl Clone for ErrorEnum { 145 | fn clone(&self) -> Self { 146 | match self { 147 | Self::Code(c) => Self::Code(c.clone()), 148 | Self::IO(e) => Self::IO(match e.raw_os_error() { 149 | Some(os) => io::Error::from_raw_os_error(os), 150 | None => e.kind().into(), 151 | }), 152 | } 153 | } 154 | } 155 | 156 | #[macro_export] 157 | macro_rules! error { 158 | ($code:ident, $($arg:tt)*) => ( 159 | Error {error: ErrorEnum::Code(ErrorCode::$code), message: format!($($arg)*)} 160 | ); 161 | } 162 | 163 | #[macro_export] 164 | macro_rules! err { 165 | ($code:ident, $($arg:tt)*) => ( 166 | Err(error!($code, $($arg)*)) 167 | ); 168 | } 169 | 170 | #[macro_export] 171 | macro_rules! errno_err { 172 | ($($arg:tt)*) => ( 173 | //panic!("{:?}", Error {error: ErrorEnum::IO(io::Error::last_os_error()), message: format!($($arg)*)}) 174 | Err(Error {error: ErrorEnum::IO(::std::io::Error::last_os_error()), message: format!($($arg)*)}) 175 | ); 176 | } 177 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | use crate::{*, util::*, common_ui::*}; 2 | use std::{collections::VecDeque, time::Instant, mem, fmt::Write, sync::atomic::{AtomicUsize, AtomicU64, Ordering}}; 3 | use core::arch::x86_64::_rdtsc; 4 | 5 | // We should rework logging. I initially thought we'd want to show a very minimal log on screen. 6 | // (Not at all like a typical server log, where you spam 1 MB/s of garbage.) 7 | // That's what this current struct is about. 8 | // But that's probably not very useful, and instead we should have two separate things: 9 | // 1. Showing nice errors in intelligently chosen places in the UI. E.g. if a binary is missing debug symbols, show that in the list of binaries. We already do this. 10 | // 2. Optionally writing various warnings to a log file, intended for the developer (me). E.g. if DWARF contains something unexpected or is missing something important. 11 | // Currently I'm using stderr for that, which may or may not be good enough. 12 | pub struct Log { 13 | pub lines: VecDeque, 14 | pub prof: Profiling, 15 | } 16 | 17 | const MAX_LINES: usize = 100; 18 | 19 | impl Log { 20 | pub fn new() -> Log { 21 | Log {lines: VecDeque::new(), prof: Profiling::new()} 22 | } 23 | 24 | pub fn add_line(&mut self, line: String) { 25 | self.lines.push_back(line); 26 | while self.lines.len() > MAX_LINES { 27 | self.lines.pop_front(); 28 | } 29 | } 30 | 31 | pub fn clear(&mut self) { 32 | self.lines.clear(); 33 | } 34 | } 35 | #[macro_export] 36 | macro_rules! log { 37 | ($log:expr, $($arg:tt)*) => ( 38 | ($log).add_line(format!($($arg)*)) 39 | ); 40 | } 41 | 42 | // A minimal profiling thing. Prints to stderr in destructor. 43 | pub struct ProfileScope { 44 | name: String, 45 | start: Instant, 46 | start_tsc: u64, 47 | total_tsc: u64, 48 | threshold_secs: f64, 49 | active: bool, 50 | multi: bool, 51 | } 52 | 53 | fn rdtsc() -> u64 { 54 | unsafe {_rdtsc()} // why is it unsafe? 55 | } 56 | 57 | impl ProfileScope { 58 | pub fn new(name: String) -> Self { 59 | ProfileScope {start: Instant::now(), start_tsc: 0, total_tsc: 0, name: name, threshold_secs: 0.0, active: true, multi: false} 60 | } 61 | 62 | pub fn with_threshold(secs: f64, name: String) -> Self { 63 | ProfileScope {start: Instant::now(), start_tsc: 0, total_tsc: 0, name: name, threshold_secs: secs, active: true, multi: false} 64 | } 65 | 66 | pub fn multi(name: String) -> Self { 67 | ProfileScope {start: Instant::now(), start_tsc: rdtsc(), total_tsc: 0, name: name, threshold_secs: 0.0, active: true, multi: true} 68 | } 69 | 70 | pub fn multi_with_threshold(secs: f64, name: String) -> Self { 71 | ProfileScope {start: Instant::now(), start_tsc: rdtsc(), total_tsc: 0, name: name, threshold_secs: secs, active: true, multi: true} 72 | } 73 | 74 | pub fn add(&mut self, tsc: u64) { 75 | assert!(self.multi); 76 | self.total_tsc += tsc; 77 | } 78 | } 79 | 80 | impl Drop for ProfileScope { 81 | fn drop(&mut self) { 82 | if !self.active { 83 | return; 84 | } 85 | let mut secs = self.start.elapsed().as_secs_f64(); 86 | if self.multi { 87 | let end_tsc = rdtsc(); 88 | let end_tsc = end_tsc.max(self.start_tsc + 1); 89 | secs = secs * (self.total_tsc as f64 / ((end_tsc - self.start_tsc) as f64)); 90 | } 91 | if self.threshold_secs <= 0.0 || secs >= self.threshold_secs { 92 | eprintln!("info: {} took {:.3}s", self.name, secs); 93 | } 94 | } 95 | } 96 | 97 | pub struct TscScope { 98 | start: u64, 99 | } 100 | impl TscScope { 101 | pub fn new() -> Self { 102 | Self {start: rdtsc()} 103 | } 104 | 105 | pub fn restart(&mut self) -> u64 { 106 | let t = rdtsc(); 107 | t.saturating_sub(mem::replace(&mut self.start, t)) 108 | } 109 | 110 | pub fn finish(mut self) -> u64 { 111 | self.restart() 112 | } 113 | } 114 | 115 | pub struct TscScopeExcludingSyscalls { 116 | scope: TscScope, 117 | prev_syscall_tsc: u64, 118 | } 119 | impl TscScopeExcludingSyscalls { 120 | pub fn new(prof: &ProfileBucket) -> Self { Self {scope: TscScope::new(), prev_syscall_tsc: prof.syscall_tsc} } 121 | 122 | pub fn restart(&mut self, prof: &ProfileBucket) -> u64 { 123 | let t = prof.syscall_tsc; 124 | self.scope.restart().saturating_sub(t.saturating_sub(mem::replace(&mut self.prev_syscall_tsc, t))) 125 | } 126 | pub fn finish(mut self, prof: &ProfileBucket) -> u64 { 127 | self.restart(prof) 128 | } 129 | } 130 | 131 | pub struct LogTimestampInDestructor(pub &'static str); 132 | impl Drop for LogTimestampInDestructor { 133 | fn drop(&mut self) { 134 | let mut t: libc::timespec; 135 | let r; 136 | unsafe { 137 | t = mem::zeroed(); 138 | r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut t as _); 139 | } 140 | assert!(r == 0); 141 | eprintln!("[{}.{:09}] {}", t.tv_sec, t.tv_nsec, self.0); 142 | } 143 | } 144 | 145 | pub fn get_thread_cpu_time_ns() -> usize { 146 | let mut ts = libc::timespec { 147 | tv_sec: 0, 148 | tv_nsec: 0, 149 | }; 150 | unsafe { 151 | libc::clock_gettime(libc::CLOCK_THREAD_CPUTIME_ID, &mut ts); 152 | } 153 | ts.tv_sec as usize * 1_000_000_000 + ts.tv_nsec as usize 154 | } 155 | 156 | pub struct ProfileBucket { 157 | // Currently not all syscalls are instrumented, only the few ones that are likely to be frequent in practice (especially syscalls that happen for each thread or for each ptrace event). 158 | pub syscall_count: usize, 159 | pub syscall_tsc: u64, 160 | pub debugger_count: usize, 161 | pub debugger_tsc: u64, 162 | pub ui_max_tsc: u64, 163 | pub other_tsc: u64, 164 | 165 | pub ui_build_max_tsc: u64, 166 | pub ui_render_max_tsc: u64, 167 | pub ui_fill_max_tsc: u64, 168 | pub ui_input_bytes: usize, 169 | pub ui_output_bytes: usize, 170 | 171 | pub s_per_tsc: f64, 172 | 173 | pub start_tsc: u64, 174 | pub start_time: Instant, 175 | pub end_tsc: u64, 176 | pub end_time: Instant, 177 | } 178 | impl ProfileBucket { 179 | pub fn new(start_time: Instant, start_tsc: u64) -> Self { 180 | let mut r = ProfileBucket::invalid(); 181 | r.start_time = start_time; 182 | r.start_tsc = start_tsc; 183 | r 184 | } 185 | 186 | pub fn invalid() -> Self { Self {start_time: unsafe {mem::zeroed()}, start_tsc: 0, syscall_count: 0, syscall_tsc: 0, debugger_count: 0, debugger_tsc: 0, ui_max_tsc: 0, s_per_tsc: 0.0, end_tsc: 0, end_time: unsafe {mem::zeroed()}, other_tsc: 0, ui_build_max_tsc: 0, ui_render_max_tsc: 0, ui_fill_max_tsc: 0, ui_input_bytes: 0, ui_output_bytes: 0} } 187 | 188 | pub fn finish(&mut self, end_time: Instant, end_tsc: u64) { 189 | assert!(self.start_tsc != 0); 190 | // rdtsc may go backwards e.g. after hibernation. 191 | let end_tsc = end_tsc.max(self.start_tsc + 1); 192 | self.end_tsc = end_tsc; 193 | self.end_time = end_time; 194 | self.s_per_tsc = (end_time - self.start_time).as_secs_f64() / (end_tsc - self.start_tsc) as f64; 195 | } 196 | } 197 | 198 | // Global counters that are periodically added to ProfileBucket and zeroed. Because passing ProfileBucket to all syscall sites would be too inconvenient (specifically for MemReader, all other sites would be fine with explicit passing of ProfileBucket). 199 | pub static SYSCALL_COUNT: AtomicUsize = AtomicUsize::new(0); 200 | pub static SYSCALL_TSC: AtomicU64 = AtomicU64::new(0); 201 | 202 | pub struct Profiling { 203 | pub buckets: VecDeque, 204 | pub bucket: ProfileBucket, 205 | } 206 | impl Profiling { 207 | pub fn new() -> Self { Self {buckets: VecDeque::new(), bucket: ProfileBucket::new(Instant::now(), rdtsc())} } 208 | 209 | // (Mutates global variables.) 210 | pub fn advance_bucket(&mut self) { 211 | // May have torn read between the two atomics. This is fine, in part because we currently only do syscalls in main thread. 212 | self.bucket.syscall_count += SYSCALL_COUNT.swap(0, Ordering::Relaxed); 213 | self.bucket.syscall_tsc += SYSCALL_TSC.swap(0, Ordering::Relaxed); 214 | let time = Instant::now(); 215 | let tsc = rdtsc(); 216 | self.bucket.finish(time, tsc); 217 | let new_bucket = ProfileBucket::new(time, tsc); 218 | self.buckets.push_back(mem::replace(&mut self.bucket, new_bucket)); 219 | 220 | while self.buckets.len() > 1000 { 221 | self.buckets.pop_front(); 222 | } 223 | } 224 | } 225 | 226 | #[macro_export] 227 | macro_rules! profile_syscall { 228 | ($($code:tt)*) => {{ 229 | let timer = TscScope::new(); 230 | let r = {$($code)*}; 231 | SYSCALL_TSC.fetch_add(timer.finish(), std::sync::atomic::Ordering::Relaxed); 232 | SYSCALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); 233 | r 234 | }}; 235 | } 236 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | bugs: 2 | u128 3 | gets stuck when debuggee is OOM-killed by linux 4 | investigate why many types are not deduped, e.g. there's DB::Block#441 5 | make stack unwinding work when control is in .plt or .plt.got, see "Recognise PLT entries such as" in libunwind code 6 | fix incorrect column numbers with tab indentation (tabs should be counted as 1 column, but we turn them into multiple spaces) 7 | fix line-wrapped copy-paste (seems to copy only the first line of selection) and line-wrapped C-k (deletes only to the end of post-wrapping line, should use pre-wrapping line) 8 | make auto-downcast check that the types are in the same language, to avoid downcasting C++ type to Rust type with same name (e.g. "parquet::format::PageHeader"); check if type decl/def matching has the same problem 9 | 10 | performance: 11 | optimize symbols loading 12 | more dynamic work distribution to threads (see util% in --load-symbols output on devserver) 13 | dynamically assign units to shards? 14 | fixup_subfunction_call_line 15 | worse locality 16 | go by units (unit groups?) instead of by data kind 17 | per-unit sort+dedup (then no dedup across units) 18 | piecewise sort 19 | dynamically pick dfs roots 20 | small buckets for shuffles in types 21 | memory usage 22 | separate allocator for temp memory to make sure it's released to OS after symbols loading 23 | madvise 24 | benchmark with file pre-evicted from page cache (using vmtouch) 25 | fix warning about unsupported forms, especially implicit_const on flags 26 | try caching chase results (71M chases, 15M unique results, 205M total DIEs) 27 | test with dwarf 4 28 | simple things found by profiler (samply is nice) 29 | look at profiler on ch server (1500 threads) 30 | test with 10k threads 31 | fix O(n^2) stuff until syscalls are the slowest part 32 | time waitpid separately 33 | waitpid is suspiciously slow; does it iterate over threads? can partially avoid it by anticipating which threads will get events? 34 | if there are enough free hw breakpoints, activate new breakpoints as hw and thread-specific right away without waiting for them to be spuriously hit by other threads 35 | maybe avoid duplicate POKEUSER calls for debug registers if the thread already had them assigned 36 | test with watches window filled with long arrays (I saw 50ms frames when there are ~5 of them; maybe just decrease length limit) 37 | investigate unresponsive ui when conditional breakpoint is hit at a high rate 38 | 39 | todo: 40 | in type search, dedup builtin types so that they're not listed once for each binary 41 | tooltip in status window with more log lines and more space for last error 42 | try with odin 43 | zig pretty-printers 44 | support gnu_debugaltlink, as found in zsh in debuginfod 45 | output window (plain text) 46 | get debuglink binaries from debuginfod 47 | reverse --tty mode: run debugger in another window and the debuggee in current window 48 | "Expected an attribute value to be a string form" on zsh 49 | buffer input when program is quitting, especially k+r and steps 50 | test with breakpad minidumps 51 | modifying debuggee variables (memory and registers), modifying ip 52 | log a message if the process forked away; do something about software breakpoints being inherited on fork and crashing the unattached child when hit 53 | add alternative default key binds for next/prev search result, F3 is not always available on mac 54 | async fetch from debuginfod (load symbols without waiting for it, then on success start over) 55 | show an error if program failed to start (e.g. executable doesn't exist) 56 | panic screen: show stack trace and process state, type something to resume+detach or kill or email stack trace or quit 57 | allow specifying cgroup and user (or maybe it's too hard, need to maybe inherit groups from parent somehow too) 58 | maybe refresh global variables in watches window on periodic timer tick when the program is running 59 | make watch window show dereferenced string-like values by default (otherwise expanding it shows the array of characters and eats all vertical space) 60 | update rust HashMap pretty printer 61 | detect and show if a pointer points to a symbol (function, global variable, vtable) 62 | do something for downcasts in conditional breakpoints (coalesce()? maybe()? ?()? ()?? checkbox to stop on error? 'is' operator? explicit checked downcast? also function to check field presense without evaluating it) 63 | `in` operator, e.g. x in [1, 2, 3] 64 | make names in MetaType etc unexpandable as array 65 | make '^' apply to expressions instead of variable names, e.g. ^(this.query_context) 66 | allow adding unconditional breakpoint on the same line as conditional one, somehow 67 | global variables from .symtab, maybe from .debug_pubnames (maybe also look into .debug_pubtypes) 68 | more built-in pretty printers: boost::multi_index sparsehash, absl, rust b-trees 69 | hotkey to switch between .h/.cpp (/.cc/.hh/.hpp/.cxx - maybe just anything after '.') 70 | maximizing windows 71 | meta functions like checking for field presence of superclass type for use in conditions 72 | key to freeze value in watches window; can also be used for controlling when to run the expression if it has side effects 73 | key to add watch value as new watch, maybe cast to type, e.g. *(0x1234 as *DB::Block) 74 | allow expressions (especially conditions) to have multiple statements separated by ';' 75 | "unexpectedly big memory read" (16 bytes) for f64 local variables on the stack in rust 76 | some kind of non-stop mode to prevent clickhouse server from losing keeper connection when stepping through the code 77 | allow navigating from source to variables (especially global) and types declared there 78 | allow to put breakpoint on a whole file (by adding breakpoint on the first line, since there's never any real code on first line in practice; have a new icon for it; internally use line 0 to cover the garbage LineInfo-s with line 0; or maybe put breakpoint only on function entry points instead of everything) 79 | handle tail calls: if you step-into a tail call (by single-stepping from instruction pointed by DW_AT_call_pc of a DW_TAG_call_site with DW_AT_call_tail_call), the tail-callee should be added to stack digest (so its stack frame is selected instead of parent) 80 | find function by address (like addr2line) 81 | use actual function name instead of "_" in namespace path, or do something else to make function-static variables usable 82 | show number of function inlined sites in disassembly window, allow setting breakpoint on it, allow opening inline-only functions 83 | follow forks, maybe even support multiple processes 84 | allow cast `foo as typeof(bar)`, also `foo as typeof(bar)::T` 85 | show argument values in stack trace 86 | hotkey to step to end of basic block or next call/ret 87 | key to follow a jump in disas 88 | allow line-based steps in .plt* (maybe by just turning them into single-instruction-steps) 89 | in disassembly window, make 'left' key jump to the start of inlined function 90 | maximizing windows (hot key and button in a corner) 91 | signal breakpoints 92 | make source window autoscroll horizontally in addition to vertically 93 | -v for build datetime 94 | handle partially-optimized-out struct values (common in rust) 95 | read color scheme from file 96 | color threads based on stack hash 97 | search in watches window 98 | search in disassembly 99 | assigning to debuggee variables and registers, including rip 100 | key to teleport ip to current line 101 | 102 | watches, expressions 103 | first, implement simple expression watches 104 | second, write many pretty printers in imaginary language, figure out what it should be; std::vector, unordered_map, unique_ptr, shared_ptr, list, optional, Rust Vec, HashMap, Option, Box, deb's Pool, etc; see if per-field format options are needed 105 | stateful expression interpreter, yield, functions 106 | associating print expressions with types (by substring match to cover templates?) 107 | always show raw value as one of the children, including for container elements (yield raw value when recursing) 108 | print options: hex, raw, array length (expression), expanded (i.e. omit array contents etc) 109 | maybe: api to output a tree of nested values, skipping subtrees if collapsed (`if $yield(val, nested)`, `$yield((), end)`?) 110 | manual refresh for slow expressions, exec time limit (press refresh to double the limit and retry) 111 | special functions/operators like sizeof() ($sizeof()?), type_name, type_offset, type_binary, offsetof, type_of (treating type as value of a special type, expandable in watch window) 112 | format matrices prettily 113 | format options for simd registers to show as vectors of given type and length (expandable) 114 | make basic types like u16 available without big search, with consistent names 115 | also consider subset of the language for injecting conditional breakpoint code 116 | 117 | unfocus search bars when moving up/down the list (but not when scrolling) 118 | show disassembly even if symbols are missing 119 | group threads by stack trace, to make it easy to exclude pool threads waiting for work 120 | show return value after step-out (and other steps that happened to step out) 121 | thread-specific breakpoints (controlled in breakpoints window), a key to lock all breakpoints to current thread 122 | resolve dynamic library call targets in disassembly, instead of showing them as "call .plt.sec+1234h" 123 | research symtab function range overlaps, don't ignore lengths (e.g. in ld-linux-x86-64.so.2, entry point is a NOTYPE symbol _start, which we incorrectly attribute to previous function _dl_help that ends just before the program entry point) 124 | pretty print variable locations (inline frame base and cfa, turn simple expressions from postfix to infix notation) 125 | handle subset of fields being optimized out (seen e.g. for metric_active_threads in ThreadPoolImpl::worker) 126 | maybe show various stop conditions (program start, crashing signals, stepping) uniformly as virtual breakpoints in breakpoints window 127 | test symbols loader with TSAN (for the sketchy bespoke locking in types traversal) 128 | parse and colorize function names, especially the template stuff 129 | parse and colorize type names, especially the template stuff 130 | 131 | try refactoring Debugger to move threads outside to avoid re-lookups everywhere 132 | show code in disassembly 133 | show variable names in disassembly 134 | allow opening files that don't exist to put breakpoints before dynamic library load 135 | locking windows 136 | test on very large disassembled functions and large source files 137 | disassembly: show basic block boundaries (jump destinations) 138 | disassembly: show current jump destination like in perf 139 | maybe handle tail calls somehow, for step-over/step-out purposes; probably show it in ui 140 | if a step is interrupted, focus on the stack frame of the step, not the top frame 141 | allow calling functions 142 | refactor loader stack to not contain leaves 143 | snapshots and "step back"; probably don't actually revert the process state, just allow inspecting snapshot of memory+registers 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /other_things/varint_benchmark.c: -------------------------------------------------------------------------------- 1 | // Compile with: gcc -g -O2 -mbmi2 -mbmi a.c 2 | 3 | // Benchmark some varint parsing implementations. 4 | // Conclusions: 5 | // * When all lengths are the same, the simple implementation wins. parse_hybrid() is a tiny bit faster. Anything more clever than that is much slower (4x) on short lengths. 6 | // * When lengths are random, branchful implementations take a 3x hit, while branchless ones don't care and win by 2x. 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define DATA_SIZE (1024 * 1024) 17 | #define NUM_RUNS 1000 18 | #define NUM_TRIALS 2 19 | 20 | inline uint64_t parse_simple(const uint8_t** ptr) { 21 | uint64_t result = 0; 22 | unsigned shift = 0; 23 | uint8_t byte; 24 | do { 25 | byte = *(*ptr)++; 26 | result |= ((uint64_t)(byte & 0x7f) << shift); 27 | shift += 7; 28 | } while (byte & 0x80); 29 | return result; 30 | } 31 | 32 | inline uint64_t collect_bits(uint64_t x) { 33 | // '#' - bit from the source, '.' - zero bit, '-' - garbage bit. 34 | // Our goal is to go from: 35 | // -#######-#######-#######-#######-#######-#######-#######-####### 36 | // to 37 | // ........######################################################## 38 | 39 | // -#######-#######-#######-#######-#######-#######-#######-####### 40 | x = ((x & 0x7f007f007f007f00) >> 1) | (x & 0x007f007f007f007f); 41 | // ..##############..##############..##############..############## 42 | x = ((x & 0xffff0000ffff0000) >> 2) | (x & 0x0000ffff0000ffff); 43 | // ....############################....############################ 44 | x = ((x & 0xffffffff00000000) >> 4) | (x & 0x00000000ffffffff); 45 | // ........######################################################## 46 | return x; 47 | } 48 | 49 | inline uint64_t collect_bits2(uint64_t x) { 50 | // '#' - bit from the source, '.' - zero bit, '-' - garbage bit. 51 | // Our goal is to go from: 52 | // -#######-#######-#######-#######-#######-#######-#######-####### 53 | // to 54 | // ........######################################################## 55 | 56 | // -#######-#######-#######-#######-#######-#######-#######-####### 57 | x = ((x & 0x7f007f007f007f00) >> 1) | (x & 0x007f007f007f007f); 58 | x = ((x & 0xffff000000000000) >> 6) | 59 | ((x & 0x0000ffff00000000) >> 4) | 60 | ((x & 0x00000000ffff0000) >> 2) | 61 | (x & 0x000000000000ffff); 62 | return x; 63 | } 64 | 65 | inline uint64_t parse_hybrid(const uint8_t** ptr) { 66 | uint64_t chunk = *(uint64_t*)*ptr; 67 | if ((chunk & 0x80) == 0) { // 1 byte 68 | *ptr += 1; 69 | return chunk & 0x7f; 70 | } else if ((chunk & 0x0000008080808080) != 0x0000008080808080) { // 2..5 bytes 71 | uint64_t res = (chunk & 0x7f) | ((chunk & 0x7f00) >> 1); 72 | *ptr += 2; 73 | if (chunk & 0x8000) { 74 | res |= (chunk & 0x7f0000) >> 2; 75 | *ptr += 1; 76 | } 77 | if ((chunk & 0x808000) == 0x808000) { 78 | res |= (chunk & 0x7f000000) >> 3; 79 | *ptr += 1; 80 | } 81 | if ((chunk & 0x80808000) == 0x80808000) { 82 | res |= (chunk & 0x7f00000000) >> 4; 83 | *ptr += 1; 84 | } 85 | return res; 86 | } else if ((chunk & 0x8080808080808080) != 0x8080808080808080) { // 6..8 bytes 87 | int bytes = _tzcnt_u64((~chunk) & 0x8080808080808080UL) >> 3; 88 | uint64_t res = collect_bits2(chunk); 89 | res &= UINT64_MAX >> (64 - 7 - bytes*7); 90 | *ptr += bytes + 1; 91 | return res; 92 | } else { // 9..10 bytes 93 | uint64_t res = collect_bits2(chunk); 94 | uint64_t high = (uint64_t)*(uint16_t*)(*ptr + 8); 95 | res |= high << 56; 96 | res &= (high << 55) | 0x7fffffffffffffff; 97 | *ptr += 9 + ((high >> 7) & 1); 98 | return res; 99 | } 100 | } 101 | 102 | inline uint64_t parse_branchless(const uint8_t** ptr) { 103 | uint64_t chunk = *(uint64_t*)*ptr; 104 | int bytes = _tzcnt_u64((~chunk) & 0x8080808080808080UL) >> 3; 105 | uint64_t res; 106 | if (bytes < 8) { 107 | res = _pext_u64(chunk, 0x7f7f7f7f7f7f7f7fUL >> (8 * (7 - bytes))); 108 | } else { 109 | res = _pext_u64(chunk, 0x7f7f7f7f7f7f7f7fUL); 110 | chunk = *(uint64_t*)(*ptr + 8); 111 | res |= (chunk & 0x7f) << 56; 112 | uint64_t extra = (chunk >> 7) & 1; 113 | bytes += extra; 114 | res |= ((chunk >> 8) & extra) << 63; 115 | } 116 | 117 | *ptr += bytes + 1; 118 | return res; 119 | } 120 | 121 | inline uint64_t parse_branchless2(const uint8_t** ptr) { 122 | uint64_t one = *(uint64_t*)*ptr; 123 | uint64_t two = *(uint64_t*)(*ptr + 8); 124 | 125 | uint64_t bytes1 = _tzcnt_u64((~one) & 0x8080808080808080UL) >> 3; 126 | uint64_t is_long = bytes1 >> 3; 127 | uint64_t res = _pext_u64(one, 0x7f7f7f7f7f7f7f7fUL >> (8 * (7 + is_long - bytes1))); 128 | 129 | uint8_t long_mask_byte = -is_long; 130 | uint64_t bytes2 = (two >> 7) & is_long; 131 | two &= (two | 0xff) >> 1; 132 | two &= long_mask_byte; 133 | res |= two << 56; 134 | 135 | uint64_t bytes = bytes1 + 1 + bytes2; 136 | *ptr += bytes; 137 | return res; 138 | } 139 | 140 | inline __m128i collect_bits_twice(__m128i x) { 141 | // Just like collect_bits(), but for two 64-bit numbers simultaneously. 142 | x = _mm_or_si128( 143 | _mm_srli_epi64(_mm_and_si128(x, _mm_set1_epi16(0x7f00)), 1), 144 | _mm_and_si128(x, _mm_set1_epi16(0x007f)) 145 | ); 146 | x = _mm_or_si128( 147 | _mm_srli_epi64(_mm_and_si128(x, _mm_set1_epi32(0xffff0000)), 2), 148 | _mm_and_si128(x, _mm_set1_epi32(0x0000ffff)) 149 | ); 150 | x = _mm_or_si128( 151 | _mm_srli_epi64(_mm_and_si128(x, _mm_set1_epi64x(0xffffffff00000000)), 4), 152 | _mm_and_si128(x, _mm_set1_epi64x(0x00000000ffffffff)) 153 | ); 154 | return x; 155 | } 156 | 157 | inline uint64_t parse_branchless3(const uint8_t** ptr) { 158 | uint64_t one = *(uint64_t*)*ptr; 159 | uint64_t two = *(uint64_t*)(*ptr + 8); 160 | 161 | uint64_t bytes1 = _tzcnt_u64((~one) & 0x8080808080808080UL) >> 3; 162 | uint64_t is_long = bytes1 >> 3; 163 | uint64_t res = collect_bits(one & (0x7f7f7f7f7f7f7f7fUL >> (8 * (7 + is_long - bytes1)))); 164 | 165 | uint8_t long_mask_byte = -is_long; 166 | uint64_t bytes2 = (two >> 7) & is_long; 167 | two &= (two | 0xff) >> 1; 168 | two &= long_mask_byte; 169 | res |= two << 56; 170 | 171 | uint64_t bytes = bytes1 + 1 + bytes2; 172 | *ptr += bytes; 173 | return res; 174 | } 175 | 176 | inline uint64_t parse_branchless4(const uint8_t** ptr) { 177 | uint64_t one = *(uint64_t*)*ptr; 178 | uint64_t two = *(uint64_t*)(*ptr + 8); 179 | 180 | uint64_t bytes1 = _tzcnt_u64((~one) & 0x8080808080808080UL) >> 3; 181 | uint64_t is_long = bytes1 >> 3; 182 | uint64_t res = collect_bits2(one & (0x7f7f7f7f7f7f7f7fUL >> (8 * (7 + is_long - bytes1)))); 183 | 184 | uint8_t long_mask_byte = -is_long; 185 | uint64_t bytes2 = (two >> 7) & is_long; 186 | two &= (two | 0xff) >> 1; 187 | two &= long_mask_byte; 188 | res |= two << 56; 189 | 190 | uint64_t bytes = bytes1 + 1 + bytes2; 191 | *ptr += bytes; 192 | return res; 193 | } 194 | 195 | inline uint64_t parse_simd(const uint8_t** ptr) { 196 | __m128i data = _mm_loadu_si128((__m128i*)*ptr); 197 | 198 | __m128i cont = _mm_and_si128(data, _mm_set1_epi8(0x80)); 199 | uint32_t cont_mask = _mm_movemask_epi8(cont); 200 | int bytes = _tzcnt_u32(~cont_mask) + 1; 201 | *ptr += bytes; 202 | 203 | __m128i halves = collect_bits_twice(data); 204 | uint64_t lo = _mm_cvtsi128_si64(halves); 205 | // Shift by one byte, so the lower byte of upper half turns into the upper byte of the result. 206 | uint64_t hi = _mm_cvtsi128_si64(_mm_srli_si128(halves, 1)); 207 | uint64_t res = lo | (hi & 0xff00000000000000); 208 | uint64_t discard_bits = 64 - bytes*7; 209 | discard_bits &= ~(discard_bits >> 32); 210 | res &= UINT64_MAX >> (uint8_t)discard_bits; 211 | return res; 212 | } 213 | 214 | // Returns number of bytes written 215 | int encode_uleb128(uint64_t value, uint8_t* ptr) { 216 | int n = 0; 217 | do { 218 | uint8_t byte = value & 0x7f; 219 | value >>= 7; 220 | if (value != 0) byte |= 0x80; 221 | *ptr++ = byte; 222 | n++; 223 | } while (value != 0); 224 | return n; 225 | } 226 | 227 | // Generate random n-bit number 228 | uint64_t rand_bits(int n) { 229 | if (n == 0) return 0; 230 | uint64_t mask = UINT64_MAX >> (64 - n); 231 | uint64_t r = 0; 232 | for (int i = 0; i < 64; i += 16) 233 | r |= ((uint64_t)rand() & 0xffff) << i; 234 | return r & mask; 235 | } 236 | 237 | void benchmark() { 238 | printf("RAND_MAX: %d\n", RAND_MAX); 239 | 240 | // Generate test data. 241 | const int NUM_DATASETS = 10; 242 | uint8_t* data[NUM_DATASETS]; 243 | uint64_t expected_sums[NUM_DATASETS]; 244 | for (int i = 0; i < NUM_DATASETS; ++i) { 245 | data[i] = aligned_alloc(16, DATA_SIZE + 100); 246 | expected_sums[i] = 0; 247 | int pos = 0; 248 | int count = 0; 249 | 250 | int bits = 7*(i+1); 251 | if (bits > 64) bits = 64; 252 | while (pos < DATA_SIZE) { 253 | if (i == 9) bits = rand() % 64 + 1; 254 | uint64_t num = rand_bits(bits); 255 | expected_sums[i] += num; 256 | pos += encode_uleb128(num, data[i] + pos); 257 | count += 1; 258 | } 259 | 260 | printf("dataset %d count: %d (avg %f bytes), sum: %lu\n", i, count, DATA_SIZE * 1. / count, expected_sums[i]); 261 | } 262 | 263 | // Benchmark 264 | for (int trial = 0; trial < NUM_TRIALS; trial++) { 265 | printf("\nTrial %d:\n", trial + 1); 266 | 267 | for (int ds = 0; ds < NUM_DATASETS; ++ds) { 268 | printf("\n Dataset %d:\n", ds); 269 | 270 | struct timespec start, end; 271 | uint64_t sum; 272 | double elapsed; 273 | 274 | #define BENCH(f) \ 275 | clock_gettime(CLOCK_MONOTONIC, &start); \ 276 | for (int run = 0; run < NUM_RUNS; run++) { \ 277 | sum = 0; \ 278 | const uint8_t* ptr = data[ds]; \ 279 | const uint8_t* end = data[ds] + DATA_SIZE; \ 280 | while (ptr < end) { \ 281 | uint64_t val = f(&ptr); \ 282 | sum += val; \ 283 | } \ 284 | } \ 285 | clock_gettime(CLOCK_MONOTONIC, &end); \ 286 | elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; \ 287 | printf(#f ": %.3f ms%s\n", elapsed * 1000, sum == expected_sums[ds] ? "" : " (sum incorrect!)"); 288 | 289 | BENCH(parse_simple); 290 | BENCH(parse_hybrid); 291 | BENCH(parse_branchless); 292 | BENCH(parse_branchless2); 293 | BENCH(parse_branchless3); 294 | //BENCH(parse_branchless4); 295 | BENCH(parse_simd); 296 | } 297 | } 298 | } 299 | 300 | void test() { 301 | uint8_t buf[100]; 302 | for (uint64_t i = 0; i < 10000000000; ++i) { 303 | if (i % 100000000 == 0) 304 | printf("... i = %#018lx\n", i); 305 | 306 | int bits = rand() % 64 + 1; 307 | uint64_t num = rand_bits(bits); 308 | 309 | int len = encode_uleb128(num, buf); 310 | 311 | const uint8_t* ptr = buf; 312 | uint64_t val = parse_hybrid(&ptr); 313 | 314 | if (ptr != buf + len || val != num) { 315 | printf("Failed on i = %lu. Expected: %#018lx (%d bytes)\nFound: %#018lx (%ld bytes)\n", i, num, len, val, ptr - buf); 316 | break; 317 | } 318 | } 319 | } 320 | 321 | int main() { 322 | //srand(time(0)); 323 | //test(); 324 | benchmark(); 325 | return 0; 326 | } 327 | -------------------------------------------------------------------------------- /other_things/demangler_exploration.rs: -------------------------------------------------------------------------------- 1 | // I gave up on this for now. 2 | // Problems: 3 | // * The mangling scheme is way too complicated. 4 | // * Even if we just want function names, we have to parse pretty much the whole grammar, because template arguments may have arbitrary complicated type names and expressions. 5 | // * Even if we want to ignore parts of the name (e.g. function arguments), we have to parse them anyway because they can be referenced by substitutions later. 6 | fn main() {} 7 | /* 8 | use std::{fmt, str}; 9 | 10 | type Result = std::result::Result; 11 | 12 | struct Ctx { 13 | out: String, 14 | } 15 | 16 | macro_rules! return_err { 17 | () => ( 18 | return Err(fmt::Error::default()); 19 | ); 20 | } 21 | 22 | macro_rules! require { 23 | ($pred:expr) => ( 24 | if !($pred) { 25 | return_err!() 26 | } 27 | ); 28 | } 29 | 30 | impl From for fmt::Error { fn from(error: str::Utf8Error) -> Self { Self::default(); } } 31 | 32 | // Function names are grammar symbols from https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling 33 | 34 | fn mangled_name(ctx: &mut Ctx, s: &mut &[u8]) -> Result<()> { 35 | // ::= _Z [ . ] 36 | 37 | require!(s.starts_with(b"_Z")); 38 | *s = &s[2..]; 39 | encoding(ctx, s)?; 40 | 41 | if !s.is_empty() { 42 | // 43 | require!(s[0] == b'.') 44 | self.out.write_str(str::from_utf8(*s)?)?; 45 | } 46 | 47 | Ok(()) 48 | } 49 | 50 | fn encoding(ctx: &mut Ctx, s: &mut &[u8]) -> Result<()> { 51 | // ::= [ ] 52 | // ::= 53 | 54 | // ::= TV # virtual table 55 | // ::= TT # VTT structure (construction vtable index) 56 | // ::= TI # typeinfo structure 57 | // ::= TS # typeinfo name (null-terminated byte string) 58 | // ::= T 59 | // ::= Tc 60 | // ::= GV # Guard variable for one-time initialization 61 | // ::= GR _ # First temporary 62 | // ::= GR _ # Subsequent temporaries 63 | // ::= GTt 64 | // ::= h _ 65 | // ::= v _ 66 | // ::= # non-virtual base override 67 | // ::= _ # virtual base override, with vcall offset 68 | 69 | require!(s.len() >= 2); 70 | let original = *s; 71 | let token = s[..2]; 72 | *s = &s[2..]; 73 | match token { 74 | b"TV" => { ctx.out.push_str("vtable "); type_(ctx, s)?; } 75 | b"TT" => { ctx.out.push_str("vtt "); type_(ctx, s)?; } 76 | b"TI" => { ctx.out.push_str("typeinfo "); type_(ctx, s)?; } 77 | b"TS" => { ctx.out.push_str("typeinfo name "); type_(ctx, s)?; } 78 | b"Th" | b"Tv" => { 79 | *s = original[1..]; 80 | ctx.out.push_str("virtual override thunk "); 81 | call_offset(ctx, s)?; 82 | ctx.out.push_str(", "); 83 | encoding(ctx, s)?; 84 | } 85 | b"Tc" => { 86 | ctx.out.push_str("virtual override thunk "); 87 | call_offset(ctx, s)?; 88 | ctx.out.push_str(", "); 89 | call_offset(ctx, s)?; 90 | ctx.out.push_str(", "); 91 | encoding(ctx, s)?; 92 | } 93 | b"GV" => { ctx.out.push_str("guard variable "); name(ctx, s)?; } 94 | b"GR" => { 95 | ctx.out.push_str("guard temporary "); 96 | name(ctx, s)?; 97 | if !s.starts_with(b"_") { 98 | let seq = seq_id(ctx, s)?; 99 | write!(ctx.out, " {}", seq); 100 | } 101 | require!(s.starts_with(b"_")); 102 | *s = &s[1..]; 103 | } 104 | //etc 105 | _ => return_err!() 106 | } 107 | } 108 | 109 | fn name(ctx: &mut Ctx, s: &mut &[u8]) -> Result<()> { 110 | // ::= N [] [] E # nested-name 111 | // ::= N [] [] E # nested-name 112 | // ::= 113 | // ::= 114 | // ::= 115 | // ::= + 116 | // ::= # class template specialization 117 | // ::= M # , variable template, initializer of a variable or data member 118 | // ::= [ ] M # , initializer of a variable or data member 119 | // ::= # template type parameter 120 | // ::= # decltype qualifier 121 | // ::= 122 | // ::= # global template 123 | // ::= # nested template 124 | // ::= # template template parameter 125 | // ::= 126 | // ::= * 127 | // ::= 128 | // ::= 129 | // ::= 130 | // ::= DC + E # structured binding declaration 131 | // ::= B 132 | // ::= C1 # complete object constructor 133 | // ::= C2 # base object constructor 134 | // ::= C3 # complete object allocating constructor 135 | // ::= CI1 # complete object inheriting constructor 136 | // ::= CI2 # base object inheriting constructor 137 | // ::= D0 # deleting destructor 138 | // ::= D1 # complete object destructor 139 | // ::= D2 # base object destructor 140 | // ::= Ut [ ] _ 141 | // ::= Ul E [ ] _ 142 | // ::= + # Parameter types or "v" if the lambda has no parameters 143 | // ::= 144 | // ::= St # ::std:: 145 | // ::= 146 | // ::= 147 | // ::= Z E [] 148 | // ::= Z E s [] 149 | // ::= Z Ed [ ] _ 150 | // ::= _ # when number < 10 151 | // ::= __ _ # when number >= 10 152 | 153 | // ::= 154 | // ::= 155 | 156 | // ::= [r] [V] [K] # restrict (C99), volatile, const 157 | // ::= (R | O) # &, && 158 | 159 | // ::= I + E 160 | // ::= # type or template 161 | // ::= X E # expression 162 | // ::= # simple expressions 163 | // ::= J * E # argument pack 164 | 165 | // ::= T [] _ 166 | 167 | // ::= 168 | // ::= 169 | 170 | // ::= <0-9A-Z>+ $ base 36 171 | 172 | // ::= [n] # 'n' is a minus sign 173 | 174 | // ::= + # types are possible return type, then parameter types 175 | 176 | // ::= 177 | // ::= 178 | // ::= 179 | // ::= 180 | // ::= 181 | // ::= 182 | // ::= 183 | // ::= 184 | // ::= 185 | // ::= P # pointer 186 | // ::= R # l-value reference 187 | // ::= O # r-value reference (C++11) 188 | // ::= C # complex pair (C99) 189 | // ::= G # imaginary (C99) 190 | // ::= 191 | // ::= Dp # pack expansion (C++11) 192 | 193 | // ::= Dt E # decltype of an id-expression or class member access (C++11) 194 | // ::= DT E # decltype of an expression (C++11) 195 | 196 | // ::= S _ 197 | // ::= S_ 198 | // ::= St # ::std:: 199 | // ::= Sa # ::std::allocator 200 | // ::= Sb # ::std::basic_string 201 | // ::= Ss # ::std::basic_string, ::std::allocator > 202 | // ::= Si # ::std::basic_istream > 203 | // ::= So # ::std::basic_ostream > 204 | // ::= Sd # ::std::basic_iostream > 205 | 206 | // ::= nw # new 207 | // ::= na # new[] 208 | // ::= dl # delete 209 | // ::= da # delete[] 210 | // ::= aw # co_await 211 | // ::= ps # + (unary) 212 | // ::= ng # - (unary) 213 | // ::= ad # & (unary) 214 | // ::= de # * (unary) 215 | // ::= co # ~ 216 | // ::= pl # + 217 | // ::= mi # - 218 | // ::= ml # * 219 | // ::= dv # / 220 | // ::= rm # % 221 | // ::= an # & 222 | // ::= or # | 223 | // ::= eo # ^ 224 | // ::= aS # = 225 | // ::= pL # += 226 | // ::= mI # -= 227 | // ::= mL # *= 228 | // ::= dV # /= 229 | // ::= rM # %= 230 | // ::= aN # &= 231 | // ::= oR # |= 232 | // ::= eO # ^= 233 | // ::= ls # << 234 | // ::= rs # >> 235 | // ::= lS # <<= 236 | // ::= rS # >>= 237 | // ::= eq # == 238 | // ::= ne # != 239 | // ::= lt # < 240 | // ::= gt # > 241 | // ::= le # <= 242 | // ::= ge # >= 243 | // ::= ss # <=> 244 | // ::= nt # ! 245 | // ::= aa # && 246 | // ::= oo # || 247 | // ::= pp # ++ (postfix in context) 248 | // ::= mm # -- (postfix in context) 249 | // ::= cm # , 250 | // ::= pm # ->* 251 | // ::= pt # -> 252 | // ::= cl # () 253 | // ::= ix # [] 254 | // ::= qu # ? 255 | // ::= cv # (cast) 256 | // ::= li # operator "" 257 | // ::= v # vendor extended operator 258 | } 259 | 260 | fn main() -> Result<()> { 261 | let mut ctx = Ctx {out: String::new()}; 262 | let s = b"_Z2hi"; 263 | let mut p = &s; 264 | mangled_name(&mut ctx, &mut p)?; 265 | println!("{}", ctx.out); 266 | Ok(()) 267 | } 268 | */ 269 | -------------------------------------------------------------------------------- /src/os.rs: -------------------------------------------------------------------------------- 1 | use libc::{pid_t}; 2 | 3 | // Linux api stuff. 4 | // * Some utils. 5 | // * Some constants and structs that are not in libc rust crate. 6 | // * Some constants and structs that are in libc crate, but have pointless minor 7 | // differences between musl and glibc versions of the "libc" rust crate. 8 | // E.g. i32 vs u32, or field names prefixed or not prefixed with "__". 9 | 10 | // sysconf(_SC_CLK_TCK), assigned at the start of main(). 11 | static mut SYSCONF_SC_CLK_TCK: usize = 0; 12 | static mut SYSCONF_PAGE_SIZE: usize = 0; 13 | static mut MY_PID: pid_t = 0; 14 | 15 | #[allow(non_snake_case)] 16 | pub fn sysconf_SC_CLK_TCK() -> usize { 17 | let r = unsafe {SYSCONF_SC_CLK_TCK}; 18 | debug_assert!(r != 0); 19 | r 20 | } 21 | #[allow(non_snake_case)] 22 | pub fn sysconf_PAGE_SIZE() -> usize { 23 | let r = unsafe {SYSCONF_PAGE_SIZE}; 24 | debug_assert!(r != 0); 25 | r 26 | } 27 | 28 | pub fn my_pid() -> pid_t { 29 | unsafe {MY_PID} 30 | } 31 | 32 | pub fn precalc_globals_os() { 33 | let assert_nonzero = |x: usize| -> usize { 34 | assert!(x != 0); 35 | x 36 | }; 37 | 38 | unsafe {SYSCONF_SC_CLK_TCK = assert_nonzero(libc::sysconf(libc::_SC_CLK_TCK) as usize)}; 39 | unsafe {SYSCONF_PAGE_SIZE = assert_nonzero(libc::sysconf(libc::_SC_PAGE_SIZE) as usize)}; 40 | unsafe {MY_PID = assert_nonzero(libc::getpid() as usize) as pid_t}; 41 | } 42 | 43 | pub const SHT_PROGBITS: u32 = 0x1; 44 | pub const SHT_SYMTAB: u32 = 0x2; 45 | pub const SHT_NOTE: u32 = 0x7; 46 | pub const SHT_NOBITS: u32 = 0x8; // pronounced as "shit! no bits!" 47 | 48 | pub const SHF_TLS: u64 = 1 << 10; 49 | pub const SHF_COMPRESSED: u64 = 1 << 11; 50 | pub const SHF_STRINGS: u64 = 1 << 5; 51 | pub const SHF_EXECINSTR: u64 = 1 << 2; 52 | 53 | pub const STT_FUNC: u8 = 2; 54 | pub const STT_OBJECT: u8 = 1; 55 | 56 | pub const SHN_UNDEF: u16 = 0; 57 | 58 | pub const PT_LOAD: u32 = 1; 59 | pub const PT_DYNAMIC: u32 = 2; 60 | pub const PT_NOTE: u32 = 4; 61 | pub const PT_GNU_EH_FRAME: u32 = 0x60000000 + 0x474e550; 62 | 63 | // Segment permissions. 64 | pub const PF_R: u32 = 0x4; 65 | pub const PF_W: u32 = 0x2; 66 | pub const PF_X: u32 = 0x1; 67 | 68 | pub const NT_GNU_BUILD_ID: u32 = 3; 69 | 70 | // These are used in core dumps. 71 | pub const NT_PRSTATUS: u32 = 1; 72 | pub const NT_PRFPREG: u32 = 2; 73 | pub const NT_PRPSINFO: u32 = 3; 74 | pub const NT_TASKSTRUCT: u32 = 4; 75 | pub const NT_AUXV: u32 = 6; 76 | pub const NT_SIGINFO: u32 = 0x53494749; 77 | pub const NT_FILE: u32 = 0x46494c45; 78 | pub const NT_PRXFPREG: u32 = 0x46e62b7f; 79 | pub const NT_X86_XSTATE: u32 = 0x202; 80 | 81 | pub const ELFCOMPRESS_ZLIB: u32 = 1; 82 | 83 | // Uuuugh. 84 | const SIGNAL_NAMES: [&str; 32] = ["[unknown signal number]", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO", "SIGPWR", "SIGSYS"]; 85 | const ERRNO_NAMES: [&str; 134] = ["[success]", "EPERM", "ENOENT", "ESRCH", "EINTR", "EIO", "ENXIO", "E2BIG", "ENOEXEC", "EBADF", "ECHILD", "EAGAIN", "ENOMEM", "EACCES", "EFAULT", "ENOTBLK", "EBUSY", "EEXIST", "EXDEV", "ENODEV", "ENOTDIR", "EISDIR", "EINVAL", "ENFILE", "EMFILE", "ENOTTY", "ETXTBSY", "EFBIG", "ENOSPC", "ESPIPE", "EROFS", "EMLINK", "EPIPE", "EDOM", "ERANGE", "EDEADLK", "ENAMETOOLONG", "ENOLCK", "ENOSYS", "ENOTEMPTY", "ELOOP", "[unknown errno]", "ENOMSG", "EIDRM", "ECHRNG", "EL2NSYNC", "EL3HLT", "EL3RST", "ELNRNG", "EUNATCH", "ENOCSI", "EL2HLT", "EBADE", "EBADR", "EXFULL", "ENOANO", "EBADRQC", "EBADSLT", "[unknown errno]", "EBFONT", "ENOSTR", "ENODATA", "ETIME", "ENOSR", "ENONET", "ENOPKG", "EREMOTE", "ENOLINK", "EADV", "ESRMNT", "ECOMM", "EPROTO", "EMULTIHOP", "EDOTDOT", "EBADMSG", "EOVERFLOW", "ENOTUNIQ", "EBADFD", "EREMCHG", "ELIBACC", "ELIBBAD", "ELIBSCN", "ELIBMAX", "ELIBEXEC", "EILSEQ", "ERESTART", "ESTRPIPE", "EUSERS", "ENOTSOCK", "EDESTADDRREQ", "EMSGSIZE", "EPROTOTYPE", "ENOPROTOOPT", "EPROTONOSUPPORT", "ESOCKTNOSUPPORT", "EOPNOTSUPP", "EPFNOSUPPORT", "EAFNOSUPPORT", "EADDRINUSE", "EADDRNOTAVAIL", "ENETDOWN", "ENETUNREACH", "ENETRESET", "ECONNABORTED", "ECONNRESET", "ENOBUFS", "EISCONN", "ENOTCONN", "ESHUTDOWN", "ETOOMANYREFS", "ETIMEDOUT", "ECONNREFUSED", "EHOSTDOWN", "EHOSTUNREACH", "EALREADY", "EINPROGRESS", "ESTALE", "EUCLEAN", "ENOTNAM", "ENAVAIL", "EISNAM", "EREMOTEIO", "EDQUOT", "ENOMEDIUM", "EMEDIUMTYPE", "ECANCELED", "ENOKEY", "EKEYEXPIRED", "EKEYREVOKED", "EKEYREJECTED", "EOWNERDEAD", "ENOTRECOVERABLE", "ERFKILL", "EHWPOISON"]; 86 | 87 | pub fn signal_name(sig: i32) -> &'static str { 88 | // strsignal() is not thread safe, and sigabbrev_np() is not in rust libc bindings. 89 | let sig = sig as usize; 90 | SIGNAL_NAMES[if sig >= SIGNAL_NAMES.len() {0} else {sig}] 91 | } 92 | 93 | pub fn errno_name(errno: i32) -> &'static str { 94 | // There's no errno -> name (not message) function that's consistenly available in C standard library on Linux. 95 | let errno = errno as usize; 96 | if errno >= ERRNO_NAMES.len() {"[unknown errno]"} else {ERRNO_NAMES[errno]} 97 | } 98 | 99 | pub fn cld_code_name(code: i32) -> &'static str { 100 | match code { 101 | libc::CLD_CONTINUED => "CLD_CONTINUED", 102 | libc::CLD_DUMPED => "CLD_DUMPED", 103 | libc::CLD_EXITED => "CLD_EXITED", 104 | libc::CLD_KILLED => "CLD_KILLED", 105 | libc::CLD_STOPPED => "CLD_STOPPED", 106 | libc::CLD_TRAPPED => "CLD_TRAPPED", 107 | 0 => "[none]", 108 | _ => "[unknown code]", 109 | } 110 | } 111 | 112 | pub fn trap_si_code_name(c: i32) -> &'static str { 113 | match c { 114 | libc::TRAP_BRANCH => "TRAP_BRANCH", 115 | libc::TRAP_BRKPT => "TRAP_BRKPT", 116 | libc::TRAP_HWBKPT => "TRAP_HWBKPT", 117 | libc::TRAP_PERF => "TRAP_PERF", 118 | libc::TRAP_TRACE => "TRAP_TRACE", 119 | libc::TRAP_UNK => "TRAP_UNK", 120 | libc::SI_ASYNCIO => "SI_ASYNCIO", 121 | libc::SI_ASYNCNL => "SI_ASYNCNL", 122 | libc::SI_DETHREAD => "SI_DETHREAD", 123 | libc::SI_KERNEL => "SI_KERNEL", 124 | libc::SI_MESGQ => "SI_MESGQ", 125 | libc::SI_QUEUE => "SI_QUEUE", 126 | libc::SI_SIGIO => "SI_SIGIO", 127 | libc::SI_TIMER => "SI_TIMER", 128 | libc::SI_TKILL => "SI_TKILL", 129 | libc::SI_USER => "SI_USER", 130 | _ => "[unknown si_code]", 131 | } 132 | } 133 | 134 | // Structs found in core dump notes. 135 | #[repr(C)] 136 | #[derive(Copy, Clone)] 137 | pub struct elf_prstatus { 138 | pub si_signo: i32, // signal number 139 | // These two seem to be reversed compared to siginfo_t. They also don't seem to be populated by binfmt_elf.c. The real siginfo is in NT_SIGINFO. 140 | pub si_code_but_actually_it_is_zero: i32, // extra code 141 | pub si_errno_but_actually_it_is_zero: i32, // errno 142 | 143 | pub pr_cursig: i16, // Current signal 144 | pub pr_sigpend: usize, // Set of pending signals 145 | pub pr_sighold: usize, // Set of held signals 146 | pub pr_pid: pid_t, 147 | pub pr_ppid: pid_t, 148 | pub pr_pgrp: pid_t, 149 | pub pr_sid: pid_t, 150 | pub pr_utime: libc::timeval, // User time 151 | pub pr_stime: libc::timeval, // System time 152 | pub pr_cutime: libc::timeval, // Cumulative user time 153 | pub pr_cstime: libc::timeval, // Cumulative system time 154 | pub pr_reg: libc::user_regs_struct, // GP registers 155 | pub pr_fpvalid: i32, // True if math co-processor being used. 156 | } 157 | #[repr(C)] 158 | #[derive(Copy, Clone)] 159 | pub struct elf_prpsinfo { 160 | pub pr_state: i8, // numeric process state 161 | pub pr_sname: i8, // char for pr_state 162 | pub pr_zomb: i8, // zombie 163 | pub pr_nice: i8, // nice val 164 | pub pr_flag: u64, // flags 165 | pub pr_uid: u32, 166 | pub pr_gid: u32, 167 | pub pr_pid: pid_t, 168 | pub pr_ppid: pid_t, 169 | pub pr_pgrp: pid_t, 170 | pub pr_sid: pid_t, 171 | pub pr_fname: [u8; 16], // filename of executable 172 | pub pr_psargs: [u8; 80], // initial part of arg list 173 | } 174 | 175 | pub const PTRACE_TRACEME: i32 = 0; 176 | pub const PTRACE_PEEKTEXT: i32 = 1; 177 | pub const PTRACE_PEEKDATA: i32 = 2; 178 | pub const PTRACE_PEEKUSER: i32 = 3; 179 | pub const PTRACE_POKETEXT: i32 = 4; 180 | pub const PTRACE_POKEDATA: i32 = 5; 181 | pub const PTRACE_POKEUSER: i32 = 6; 182 | pub const PTRACE_CONT: i32 = 7; 183 | pub const PTRACE_KILL: i32 = 8; 184 | pub const PTRACE_SINGLESTEP: i32 = 9; 185 | pub const PTRACE_GETREGS: i32 = 12; 186 | pub const PTRACE_SETREGS: i32 = 13; 187 | pub const PTRACE_GETFPREGS: i32 = 14; 188 | pub const PTRACE_SETFPREGS: i32 = 15; 189 | pub const PTRACE_ATTACH: i32 = 16; 190 | pub const PTRACE_DETACH: i32 = 17; 191 | pub const PTRACE_GETFPXREGS: i32 = 18; 192 | pub const PTRACE_SETFPXREGS: i32 = 19; 193 | pub const PTRACE_SYSCALL: i32 = 24; 194 | pub const PTRACE_GET_THREAD_AREA: i32 = 25; 195 | pub const PTRACE_SET_THREAD_AREA: i32 = 26; 196 | pub const PTRACE_ARCH_PRCTL: i32 = 30; 197 | pub const PTRACE_SYSEMU: i32 = 31; 198 | pub const PTRACE_SYSEMU_SINGLESTEP: i32 = 32; 199 | pub const PTRACE_SINGLEBLOCK: i32 = 33; 200 | pub const PTRACE_SETOPTIONS: i32 = 0x4200; 201 | pub const PTRACE_GETEVENTMSG: i32 = 0x4201; 202 | pub const PTRACE_GETSIGINFO: i32 = 0x4202; 203 | pub const PTRACE_SETSIGINFO: i32 = 0x4203; 204 | pub const PTRACE_GETREGSET: i32 = 0x4204; 205 | pub const PTRACE_SETREGSET: i32 = 0x4205; 206 | pub const PTRACE_SEIZE: i32 = 0x4206; 207 | pub const PTRACE_INTERRUPT: i32 = 0x4207; 208 | pub const PTRACE_LISTEN: i32 = 0x4208; 209 | pub const PTRACE_PEEKSIGINFO: i32 = 0x4209; 210 | pub const PTRACE_GETSIGMASK: i32 = 0x420a; 211 | pub const PTRACE_SETSIGMASK: i32 = 0x420b; 212 | pub const PTRACE_SECCOMP_GET_FILTER: i32 = 0x420c; 213 | pub const PTRACE_SECCOMP_GET_METADATA: i32 = 0x420d; 214 | pub const PTRACE_GET_SYSCALL_INFO: i32 = 0x420e; 215 | pub const PTRACE_GET_RSEQ_CONFIGURATION: i32 = 0x420f; 216 | pub const PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG: i32 = 0x4210; 217 | pub const PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG: i32 = 0x4211; 218 | 219 | pub fn ptrace_request_name(c: i32) -> &'static str { 220 | match c { 221 | PTRACE_TRACEME => "PTRACE_TRACEME", PTRACE_PEEKTEXT => "PTRACE_PEEKTEXT", PTRACE_PEEKDATA => "PTRACE_PEEKDATA", PTRACE_PEEKUSER => "PTRACE_PEEKUSER", PTRACE_POKETEXT => "PTRACE_POKETEXT", PTRACE_POKEDATA => "PTRACE_POKEDATA", PTRACE_POKEUSER => "PTRACE_POKEUSER", PTRACE_CONT => "PTRACE_CONT", PTRACE_KILL => "PTRACE_KILL", PTRACE_SINGLESTEP => "PTRACE_SINGLESTEP", PTRACE_GETREGS => "PTRACE_GETREGS", PTRACE_SETREGS => "PTRACE_SETREGS", PTRACE_GETFPREGS => "PTRACE_GETFPREGS", PTRACE_SETFPREGS => "PTRACE_SETFPREGS", PTRACE_ATTACH => "PTRACE_ATTACH", PTRACE_DETACH => "PTRACE_DETACH", PTRACE_GETFPXREGS => "PTRACE_GETFPXREGS", PTRACE_SETFPXREGS => "PTRACE_SETFPXREGS", PTRACE_SYSCALL => "PTRACE_SYSCALL", PTRACE_GET_THREAD_AREA => "PTRACE_GET_THREAD_AREA", PTRACE_SET_THREAD_AREA => "PTRACE_SET_THREAD_AREA", PTRACE_ARCH_PRCTL => "PTRACE_ARCH_PRCTL", PTRACE_SYSEMU => "PTRACE_SYSEMU", PTRACE_SYSEMU_SINGLESTEP => "PTRACE_SYSEMU_SINGLESTEP", PTRACE_SINGLEBLOCK => "PTRACE_SINGLEBLOCK", PTRACE_SETOPTIONS => "PTRACE_SETOPTIONS", PTRACE_GETEVENTMSG => "PTRACE_GETEVENTMSG", PTRACE_GETSIGINFO => "PTRACE_GETSIGINFO", PTRACE_SETSIGINFO => "PTRACE_SETSIGINFO", PTRACE_GETREGSET => "PTRACE_GETREGSET", PTRACE_SETREGSET => "PTRACE_SETREGSET", PTRACE_SEIZE => "PTRACE_SEIZE", PTRACE_INTERRUPT => "PTRACE_INTERRUPT", PTRACE_LISTEN => "PTRACE_LISTEN", PTRACE_PEEKSIGINFO => "PTRACE_PEEKSIGINFO", PTRACE_GETSIGMASK => "PTRACE_GETSIGMASK", PTRACE_SETSIGMASK => "PTRACE_SETSIGMASK", PTRACE_SECCOMP_GET_FILTER => "PTRACE_SECCOMP_GET_FILTER", PTRACE_SECCOMP_GET_METADATA => "PTRACE_SECCOMP_GET_METADATA", PTRACE_GET_SYSCALL_INFO => "PTRACE_GET_SYSCALL_INFO", PTRACE_GET_RSEQ_CONFIGURATION => "PTRACE_GET_RSEQ_CONFIGURATION", PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG => "PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG", PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG => "PTRACE_GET_SYSCALL_USER_DISPATCH_CONFIG", 222 | _ => "[unknown request]", 223 | } 224 | } 225 | 226 | pub const PTRACE_EVENT_FORK: i32 = 1; 227 | pub const PTRACE_EVENT_VFORK: i32 = 2; 228 | pub const PTRACE_EVENT_CLONE: i32 = 3; 229 | pub const PTRACE_EVENT_EXEC: i32 = 4; 230 | pub const PTRACE_EVENT_VFORK_DONE: i32 = 5; 231 | pub const PTRACE_EVENT_EXIT: i32 = 6; 232 | pub const PTRACE_EVENT_SECCOMP: i32 = 7; 233 | pub const PTRACE_EVENT_STOP: i32 = 128; 234 | 235 | pub const PTRACE_O_TRACESYSGOOD: u64 = 1; 236 | pub const PTRACE_O_TRACEFORK: u64 = 1 << PTRACE_EVENT_FORK; 237 | pub const PTRACE_O_TRACEVFORK: u64 = 1 << PTRACE_EVENT_VFORK; 238 | pub const PTRACE_O_TRACECLONE: u64 = 1 << PTRACE_EVENT_CLONE; 239 | pub const PTRACE_O_TRACEEXEC: u64 = 1 << PTRACE_EVENT_EXEC; 240 | pub const PTRACE_O_TRACEVFORKDONE: u64 = 1 << PTRACE_EVENT_VFORK_DONE; 241 | pub const PTRACE_O_TRACEEXIT: u64 = 1 << PTRACE_EVENT_EXIT; 242 | pub const PTRACE_O_TRACESECCOMP: u64 = 1 << PTRACE_EVENT_SECCOMP; 243 | pub const PTRACE_O_EXITKILL: u64 = 1 << 20; 244 | pub const PTRACE_O_SUSPEND_SECCOMP: u64 = 1 << 21; 245 | -------------------------------------------------------------------------------- /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 = "base64" 13 | version = "0.22.1" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 16 | 17 | [[package]] 18 | name = "bitflags" 19 | version = "1.3.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 22 | 23 | [[package]] 24 | name = "bytes" 25 | version = "1.10.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" 28 | 29 | [[package]] 30 | name = "cc" 31 | version = "1.2.15" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" 34 | dependencies = [ 35 | "shlex", 36 | ] 37 | 38 | [[package]] 39 | name = "cfg-if" 40 | version = "1.0.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 43 | 44 | [[package]] 45 | name = "cpp_demangle" 46 | version = "0.5.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def" 49 | dependencies = [ 50 | "cfg-if", 51 | ] 52 | 53 | [[package]] 54 | name = "crc32fast" 55 | version = "1.3.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 58 | dependencies = [ 59 | "cfg-if", 60 | ] 61 | 62 | [[package]] 63 | name = "equivalent" 64 | version = "1.0.1" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 67 | 68 | [[package]] 69 | name = "fallible-iterator" 70 | version = "0.3.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 73 | 74 | [[package]] 75 | name = "flate2" 76 | version = "1.0.35" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" 79 | dependencies = [ 80 | "crc32fast", 81 | "miniz_oxide", 82 | ] 83 | 84 | [[package]] 85 | name = "fnv" 86 | version = "1.0.7" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 89 | 90 | [[package]] 91 | name = "getrandom" 92 | version = "0.2.10" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" 95 | dependencies = [ 96 | "cfg-if", 97 | "libc", 98 | "wasi", 99 | ] 100 | 101 | [[package]] 102 | name = "gimli" 103 | version = "0.31.1" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 106 | dependencies = [ 107 | "fallible-iterator", 108 | "indexmap", 109 | "stable_deref_trait", 110 | ] 111 | 112 | [[package]] 113 | name = "hashbrown" 114 | version = "0.15.2" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 117 | 118 | [[package]] 119 | name = "http" 120 | version = "1.2.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" 123 | dependencies = [ 124 | "bytes", 125 | "fnv", 126 | "itoa", 127 | ] 128 | 129 | [[package]] 130 | name = "httparse" 131 | version = "1.10.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" 134 | 135 | [[package]] 136 | name = "iced-x86" 137 | version = "1.19.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "b7cc8d38244d84278262c8ebe6930cc44283d194cbabae2651f6112103802fb5" 140 | dependencies = [ 141 | "lazy_static", 142 | ] 143 | 144 | [[package]] 145 | name = "indexmap" 146 | version = "2.6.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 149 | dependencies = [ 150 | "equivalent", 151 | "hashbrown", 152 | ] 153 | 154 | [[package]] 155 | name = "itoa" 156 | version = "1.0.14" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 159 | 160 | [[package]] 161 | name = "lazy_static" 162 | version = "1.4.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 165 | 166 | [[package]] 167 | name = "libc" 168 | version = "0.2.170" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" 171 | 172 | [[package]] 173 | name = "log" 174 | version = "0.4.26" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" 177 | 178 | [[package]] 179 | name = "md5" 180 | version = "0.7.0" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 183 | 184 | [[package]] 185 | name = "miniz_oxide" 186 | version = "0.8.5" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" 189 | dependencies = [ 190 | "adler2", 191 | ] 192 | 193 | [[package]] 194 | name = "nnd" 195 | version = "0.65.0" 196 | dependencies = [ 197 | "bitflags", 198 | "cpp_demangle", 199 | "crc32fast", 200 | "flate2", 201 | "gimli", 202 | "iced-x86", 203 | "libc", 204 | "md5", 205 | "rand", 206 | "rustc-demangle", 207 | "tikv-jemallocator", 208 | "unicode-segmentation", 209 | "unicode-width", 210 | "ureq", 211 | "voracious_radix_sort", 212 | ] 213 | 214 | [[package]] 215 | name = "once_cell" 216 | version = "1.20.3" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 219 | 220 | [[package]] 221 | name = "percent-encoding" 222 | version = "2.3.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 225 | 226 | [[package]] 227 | name = "ppv-lite86" 228 | version = "0.2.17" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 231 | 232 | [[package]] 233 | name = "rand" 234 | version = "0.8.5" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 237 | dependencies = [ 238 | "libc", 239 | "rand_chacha", 240 | "rand_core", 241 | ] 242 | 243 | [[package]] 244 | name = "rand_chacha" 245 | version = "0.3.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 248 | dependencies = [ 249 | "ppv-lite86", 250 | "rand_core", 251 | ] 252 | 253 | [[package]] 254 | name = "rand_core" 255 | version = "0.6.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 258 | dependencies = [ 259 | "getrandom", 260 | ] 261 | 262 | [[package]] 263 | name = "ring" 264 | version = "0.17.11" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" 267 | dependencies = [ 268 | "cc", 269 | "cfg-if", 270 | "getrandom", 271 | "libc", 272 | "untrusted", 273 | "windows-sys", 274 | ] 275 | 276 | [[package]] 277 | name = "rustc-demangle" 278 | version = "0.1.23" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 281 | 282 | [[package]] 283 | name = "rustls" 284 | version = "0.23.23" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" 287 | dependencies = [ 288 | "log", 289 | "once_cell", 290 | "ring", 291 | "rustls-pki-types", 292 | "rustls-webpki", 293 | "subtle", 294 | "zeroize", 295 | ] 296 | 297 | [[package]] 298 | name = "rustls-pemfile" 299 | version = "2.2.0" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 302 | dependencies = [ 303 | "rustls-pki-types", 304 | ] 305 | 306 | [[package]] 307 | name = "rustls-pki-types" 308 | version = "1.11.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 311 | 312 | [[package]] 313 | name = "rustls-webpki" 314 | version = "0.102.8" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 317 | dependencies = [ 318 | "ring", 319 | "rustls-pki-types", 320 | "untrusted", 321 | ] 322 | 323 | [[package]] 324 | name = "shlex" 325 | version = "1.3.0" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 328 | 329 | [[package]] 330 | name = "stable_deref_trait" 331 | version = "1.2.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 334 | 335 | [[package]] 336 | name = "subtle" 337 | version = "2.6.1" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 340 | 341 | [[package]] 342 | name = "tikv-jemalloc-sys" 343 | version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" 346 | dependencies = [ 347 | "cc", 348 | "libc", 349 | ] 350 | 351 | [[package]] 352 | name = "tikv-jemallocator" 353 | version = "0.6.1" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" 356 | dependencies = [ 357 | "libc", 358 | "tikv-jemalloc-sys", 359 | ] 360 | 361 | [[package]] 362 | name = "unicode-segmentation" 363 | version = "1.10.1" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" 366 | 367 | [[package]] 368 | name = "unicode-width" 369 | version = "0.1.10" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 372 | 373 | [[package]] 374 | name = "untrusted" 375 | version = "0.9.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 378 | 379 | [[package]] 380 | name = "ureq" 381 | version = "3.0.6" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "ca2e2dbdf4e95780e5d41804fab88b928a24585721018409eb429b1d29356bde" 384 | dependencies = [ 385 | "base64", 386 | "flate2", 387 | "log", 388 | "percent-encoding", 389 | "rustls", 390 | "rustls-pemfile", 391 | "rustls-pki-types", 392 | "ureq-proto", 393 | "utf-8", 394 | "webpki-roots", 395 | ] 396 | 397 | [[package]] 398 | name = "ureq-proto" 399 | version = "0.3.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "2c51fe73e1d8c4e06bb2698286f7e7453c6fc90528d6d2e7fc36bb4e87fe09b1" 402 | dependencies = [ 403 | "base64", 404 | "http", 405 | "httparse", 406 | "log", 407 | ] 408 | 409 | [[package]] 410 | name = "utf-8" 411 | version = "0.7.6" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" 414 | 415 | [[package]] 416 | name = "voracious_radix_sort" 417 | version = "1.2.0" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "446e7ffcb6c27a71d05af7e51ef2ee5b71c48424b122a832f2439651e1914899" 420 | 421 | [[package]] 422 | name = "wasi" 423 | version = "0.11.0+wasi-snapshot-preview1" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 426 | 427 | [[package]] 428 | name = "webpki-roots" 429 | version = "0.26.8" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" 432 | dependencies = [ 433 | "rustls-pki-types", 434 | ] 435 | 436 | [[package]] 437 | name = "windows-sys" 438 | version = "0.52.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 441 | dependencies = [ 442 | "windows-targets", 443 | ] 444 | 445 | [[package]] 446 | name = "windows-targets" 447 | version = "0.52.6" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 450 | dependencies = [ 451 | "windows_aarch64_gnullvm", 452 | "windows_aarch64_msvc", 453 | "windows_i686_gnu", 454 | "windows_i686_gnullvm", 455 | "windows_i686_msvc", 456 | "windows_x86_64_gnu", 457 | "windows_x86_64_gnullvm", 458 | "windows_x86_64_msvc", 459 | ] 460 | 461 | [[package]] 462 | name = "windows_aarch64_gnullvm" 463 | version = "0.52.6" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 466 | 467 | [[package]] 468 | name = "windows_aarch64_msvc" 469 | version = "0.52.6" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 472 | 473 | [[package]] 474 | name = "windows_i686_gnu" 475 | version = "0.52.6" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 478 | 479 | [[package]] 480 | name = "windows_i686_gnullvm" 481 | version = "0.52.6" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 484 | 485 | [[package]] 486 | name = "windows_i686_msvc" 487 | version = "0.52.6" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 490 | 491 | [[package]] 492 | name = "windows_x86_64_gnu" 493 | version = "0.52.6" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 496 | 497 | [[package]] 498 | name = "windows_x86_64_gnullvm" 499 | version = "0.52.6" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 502 | 503 | [[package]] 504 | name = "windows_x86_64_msvc" 505 | version = "0.52.6" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 508 | 509 | [[package]] 510 | name = "zeroize" 511 | version = "1.8.1" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 514 | -------------------------------------------------------------------------------- /other_things/some_dwarf_stats.txt: -------------------------------------------------------------------------------- 1 | dwarf4 2 | [src/bin/dwarf_exploration.rs:682] units = 5815 3 | [src/bin/dwarf_exploration.rs:683] dies = 98656533 4 | [src/bin/dwarf_exploration.rs:684] attrs = 323283313 5 | [src/bin/dwarf_exploration.rs:685] funcs_including_decls_and_inline = 13391865 6 | [src/bin/dwarf_exploration.rs:686] funcs = 579365 7 | [src/bin/dwarf_exploration.rs:687] deduped_funcs = 355413 8 | [src/bin/dwarf_exploration.rs:688] template_funcs as f64 / funcs as f64 = 0.18408775124489743 9 | [src/bin/dwarf_exploration.rs:689] funcs_without_name as f64 / funcs as f64 = 0.008288384697038999 10 | [src/bin/dwarf_exploration.rs:690] funcs_without_link_name as f64 / funcs as f64 = 0.0454463075953846 11 | [src/bin/dwarf_exploration.rs:691] funcs_with_multiple_ranges = 0 12 | [src/bin/dwarf_exploration.rs:692] func_ranges = 579365 13 | [src/bin/dwarf_exploration.rs:693] vars = 23414680 14 | [src/bin/dwarf_exploration.rs:694] locs = 30913530 15 | [src/bin/dwarf_exploration.rs:695] types = 2150699 16 | [src/bin/dwarf_exploration.rs:696] template_types as f64 / types as f64 = 0.644047818871911 17 | [src/bin/dwarf_exploration.rs:697] fields = 2118337 18 | [src/bin/dwarf_exploration.rs:698] inlined_funcs = 12812233 19 | [src/bin/dwarf_exploration.rs:699] inlined_func_ranges = 16413872 20 | [src/bin/dwarf_exploration.rs:700] inlined_funcs_without_ranges = 0 21 | [src/bin/dwarf_exploration.rs:701] total_func_link_name_len = 73269012 22 | [src/bin/dwarf_exploration.rs:702] total_func_full_name_len = 25915453 23 | [src/bin/dwarf_exploration.rs:703] total_func_local_name_len = 24997566 24 | [src/bin/dwarf_exploration.rs:704] total_var_name_len = 127335519 25 | [src/bin/dwarf_exploration.rs:705] total_type_full_name_len = 278636616 26 | [src/bin/dwarf_exploration.rs:706] longest_origin_chain = 4 27 | [src/bin/dwarf_exploration.rs:707] vars_without_loc = 3384588 28 | [src/bin/dwarf_exploration.rs:708] func_instr_len = 175790195 29 | 30 | dwarf4-debug 31 | .debug_info pre-pass took 2.159s 32 | [src/bin/dwarf_exploration.rs:152] units_vec.len() = 8790 33 | [src/bin/dwarf_exploration.rs:153] line_programs.len() = 8790 34 | ----- 35 | .debug_info took 135.569s 36 | [src/bin/dwarf_exploration.rs:801] dies = 137053555 37 | [src/bin/dwarf_exploration.rs:802] attrs = 466609646 38 | [src/bin/dwarf_exploration.rs:803] funcs_including_decls_and_inline = 28778326 39 | [src/bin/dwarf_exploration.rs:804] funcs = 7491180 40 | [src/bin/dwarf_exploration.rs:805] deduped_funcs = 2656256 41 | [src/bin/dwarf_exploration.rs:806] template_funcs as f64 / funcs as f64 = 0.3600713105278474 42 | [src/bin/dwarf_exploration.rs:807] funcs_without_name as f64 / funcs as f64 = 0.00149509156100908 43 | [src/bin/dwarf_exploration.rs:808] funcs_without_link_name as f64 / funcs as f64 = 0.008073761410084927 44 | [src/bin/dwarf_exploration.rs:809] funcs_with_multiple_ranges = 0 45 | [src/bin/dwarf_exploration.rs:810] func_ranges = 7491180 46 | [src/bin/dwarf_exploration.rs:811] vars = 21505169 47 | [src/bin/dwarf_exploration.rs:812] locs = 21792346 48 | [src/bin/dwarf_exploration.rs:813] types = 4134047 49 | [src/bin/dwarf_exploration.rs:814] template_types as f64 / types as f64 = 0.6572588555476027 50 | [src/bin/dwarf_exploration.rs:815] fields = 3818290 51 | [src/bin/dwarf_exploration.rs:816] inlined_funcs = 1010871 52 | [src/bin/dwarf_exploration.rs:817] inlined_func_ranges = 1189096 53 | [src/bin/dwarf_exploration.rs:818] inlined_funcs_without_ranges = 0 54 | [src/bin/dwarf_exploration.rs:819] total_func_link_name_len = 1001318382 55 | [src/bin/dwarf_exploration.rs:820] total_func_full_name_len = 537511322 56 | [src/bin/dwarf_exploration.rs:821] total_func_local_name_len = 522763395 57 | [src/bin/dwarf_exploration.rs:822] total_var_name_len = 134760343 58 | [src/bin/dwarf_exploration.rs:823] total_type_full_name_len = 573185069 59 | [src/bin/dwarf_exploration.rs:824] longest_origin_chain = 4 60 | [src/bin/dwarf_exploration.rs:825] vars_without_loc = 224791 61 | [src/bin/dwarf_exploration.rs:826] func_instr_len = 892392252 62 | 63 | [src/bin/dwarf_exploration.rs:828] cross_unit_type_refs = 0 64 | [src/bin/dwarf_exploration.rs:829] forward_type_refs = 58683619 65 | [src/bin/dwarf_exploration.rs:830] forward_base_type_refs = 4611213 66 | [src/bin/dwarf_exploration.rs:831] forward_simple_type_refs = 49687503 67 | [src/bin/dwarf_exploration.rs:832] forward_composite_type_refs = 4380330 68 | [src/bin/dwarf_exploration.rs:833] base_type_refs = 11608859 69 | [src/bin/dwarf_exploration.rs:834] simple_type_refs = 75833405 70 | [src/bin/dwarf_exploration.rs:835] composite_type_refs = 19570815 71 | [src/bin/dwarf_exploration.rs:836] ptr_to_member_type_refs = 5773 72 | [src/bin/dwarf_exploration.rs:837] subrange_type_refs = 0 73 | [src/bin/dwarf_exploration.rs:838] unrecognized_type_refs = 0 74 | [src/bin/dwarf_exploration.rs:839] type_refs = 107018852 75 | [src/bin/dwarf_exploration.rs:840] type_refs_with_specification = 0 76 | [src/bin/dwarf_exploration.rs:841] type_refs_with_abstract_origin = 0 77 | [src/bin/dwarf_exploration.rs:842] data_member_loc_simple = 3818462 78 | [src/bin/dwarf_exploration.rs:843] data_member_loc_expr = 610 79 | [src/bin/dwarf_exploration.rs:844] nontrivial_inheritance_loc = 610 80 | [src/bin/dwarf_exploration.rs:845] data_member_loc_list = 0 81 | ----- 82 | [src/bin/dwarf_exploration.rs:1095] all_anchors.len() = 17451800 83 | [src/bin/dwarf_exploration.rs:1098] all_anchors.len() = 4505190 84 | [src/bin/dwarf_exploration.rs:1115] longest_run_without_anchors = 314536 85 | sorting anchors took 0.234s 86 | 87 | 88 | dwarf4-official-release 89 | .eh_frame took 2.939s 90 | [src/bin/dwarf_exploration.rs:108] cies.len() = 4 91 | [src/bin/dwarf_exploration.rs:109] fde_count = 532671 92 | [src/bin/dwarf_exploration.rs:110] row_count = 4639889 93 | [src/bin/dwarf_exploration.rs:111] bytes_covered_by_fdes = 293714860 94 | [src/bin/dwarf_exploration.rs:112] bytes_covered_by_rows = 293714860 95 | [src/bin/dwarf_exploration.rs:113] register_rules = 17476464 96 | [src/bin/dwarf_exploration.rs:114] max_rows_per_fde = 2501 97 | ----- 98 | .debug_info pre-pass took 4.765s 99 | [src/bin/dwarf_exploration.rs:150] units_vec.len() = 33109 100 | [src/bin/dwarf_exploration.rs:151] line_programs.len() = 33109 101 | ----- 102 | .debug_info took 39.317s 103 | [src/bin/dwarf_exploration.rs:730] dies = 83018884 104 | [src/bin/dwarf_exploration.rs:731] attrs = 274861560 105 | [src/bin/dwarf_exploration.rs:732] funcs_including_decls_and_inline = 12236632 106 | [src/bin/dwarf_exploration.rs:733] funcs = 316368 107 | [src/bin/dwarf_exploration.rs:734] deduped_funcs = 316355 108 | [src/bin/dwarf_exploration.rs:735] template_funcs as f64 / funcs as f64 = 0.08716115409902392 109 | [src/bin/dwarf_exploration.rs:736] funcs_without_name as f64 / funcs as f64 = 0.010901861123754615 110 | [src/bin/dwarf_exploration.rs:737] funcs_without_link_name as f64 / funcs as f64 = 0.06794935012390634 111 | [src/bin/dwarf_exploration.rs:738] funcs_with_multiple_ranges = 0 112 | [src/bin/dwarf_exploration.rs:739] func_ranges = 316368 113 | [src/bin/dwarf_exploration.rs:740] vars = 16778955 114 | [src/bin/dwarf_exploration.rs:741] locs = 21236601 115 | [src/bin/dwarf_exploration.rs:742] types = 2302458 116 | [src/bin/dwarf_exploration.rs:743] template_types as f64 / types as f64 = 0.5687130883603523 117 | [src/bin/dwarf_exploration.rs:744] fields = 3062095 118 | [src/bin/dwarf_exploration.rs:745] inlined_funcs = 9474795 119 | [src/bin/dwarf_exploration.rs:746] inlined_func_ranges = 12344834 120 | [src/bin/dwarf_exploration.rs:747] inlined_funcs_without_ranges = 0 121 | [src/bin/dwarf_exploration.rs:748] total_func_link_name_len = 40081317 122 | [src/bin/dwarf_exploration.rs:749] total_func_full_name_len = 12118588 123 | [src/bin/dwarf_exploration.rs:750] total_func_local_name_len = 11534211 124 | [src/bin/dwarf_exploration.rs:751] total_var_name_len = 110086268 125 | [src/bin/dwarf_exploration.rs:752] total_type_full_name_len = 274056042 126 | [src/bin/dwarf_exploration.rs:753] longest_origin_chain = 4 127 | [src/bin/dwarf_exploration.rs:754] vars_without_loc = 3123063 128 | [src/bin/dwarf_exploration.rs:755] func_instr_len = 133758066 129 | ----- 130 | .debug_line took 1.297s 131 | [src/bin/dwarf_exploration.rs:972] num_empty_sequences = 0 132 | [src/bin/dwarf_exploration.rs:973] num_sequences_starting_at_zero = 17 133 | [src/bin/dwarf_exploration.rs:974] max_address_in_sequences_starting_at_zero = 75 134 | [src/bin/dwarf_exploration.rs:975] num_sequences = 316316 135 | [src/bin/dwarf_exploration.rs:976] num_all_rows = 18388458 136 | [src/bin/dwarf_exploration.rs:977] num_rows = 18388399 137 | [src/bin/dwarf_exploration.rs:978] num_include_directories = 225159 138 | [src/bin/dwarf_exploration.rs:979] num_files = 777839 139 | [src/bin/dwarf_exploration.rs:980] file_to_idx.len() = 13378 140 | [src/bin/dwarf_exploration.rs:981] kinda_num_statements = 11788032 141 | [src/bin/dwarf_exploration.rs:982] kinda_num_basic_blocks = 0 142 | [src/bin/dwarf_exploration.rs:983] kinda_num_prologues = 315641 143 | [src/bin/dwarf_exploration.rs:984] kinda_num_epilogues = 211680 144 | [src/bin/dwarf_exploration.rs:985] num_programs_where_files_have_md5 = 0 145 | [src/bin/dwarf_exploration.rs:986] addr_bytes_covered = 133989751 146 | [src/bin/dwarf_exploration.rs:987] total_program_header_bytes = 21784689 147 | [src/bin/dwarf_exploration.rs:988] total_program_bytes = 110081149 148 | [src/bin/dwarf_exploration.rs:989] max_program_header_bytes = 17139 149 | [src/bin/dwarf_exploration.rs:990] max_program_bytes = 3713091 150 | [src/bin/dwarf_exploration.rs:991] max_include_directories = 122 151 | [src/bin/dwarf_exploration.rs:992] max_files = 703 152 | [src/bin/dwarf_exploration.rs:993] max_rows_per_program = 588310 153 | [src/bin/dwarf_exploration.rs:994] max_rows_per_sequence = 29046 154 | [src/bin/dwarf_exploration.rs:995] duplicate_sequences = 0 155 | [src/bin/dwarf_exploration.rs:996] overlapping_sequences = 0 156 | [src/bin/dwarf_exploration.rs:997] deduped_addr_bytes_covered = 133989751 157 | [src/bin/dwarf_exploration.rs:998] mem::size_of::() = 64 158 | [src/bin/dwarf_exploration.rs:999] max_line = 239378 159 | [src/bin/dwarf_exploration.rs:1000] max_column = 368 160 | [src/bin/dwarf_exploration.rs:1005] all_anchors.len() = 38042814 161 | [src/bin/dwarf_exploration.rs:1008] all_anchors.len() = 23140727 162 | [src/bin/dwarf_exploration.rs:1025] longest_run_without_anchors = 222016 163 | sorting anchors took 1.431s 164 | 165 | dwarf5 166 | [src/bin/dwarf_exploration.rs:682] units = 5815 167 | [src/bin/dwarf_exploration.rs:683] dies = 98669598 168 | [src/bin/dwarf_exploration.rs:684] attrs = 323438477 169 | [src/bin/dwarf_exploration.rs:685] funcs_including_decls_and_inline = 13391865 170 | [src/bin/dwarf_exploration.rs:686] funcs = 579365 171 | [src/bin/dwarf_exploration.rs:687] deduped_funcs = 355413 172 | [src/bin/dwarf_exploration.rs:688] template_funcs as f64 / funcs as f64 = 0.18408775124489743 173 | [src/bin/dwarf_exploration.rs:689] funcs_without_name as f64 / funcs as f64 = 0.008288384697038999 174 | [src/bin/dwarf_exploration.rs:690] funcs_without_link_name as f64 / funcs as f64 = 0.0454463075953846 175 | [src/bin/dwarf_exploration.rs:691] funcs_with_multiple_ranges = 0 176 | [src/bin/dwarf_exploration.rs:692] func_ranges = 579365 177 | [src/bin/dwarf_exploration.rs:693] vars = 23414680 178 | [src/bin/dwarf_exploration.rs:694] locs = 30913530 179 | [src/bin/dwarf_exploration.rs:695] types = 2150699 180 | [src/bin/dwarf_exploration.rs:696] template_types as f64 / types as f64 = 0.644047818871911 181 | [src/bin/dwarf_exploration.rs:697] fields = 2118337 182 | [src/bin/dwarf_exploration.rs:698] inlined_funcs = 12812233 183 | [src/bin/dwarf_exploration.rs:699] inlined_func_ranges = 16413872 184 | [src/bin/dwarf_exploration.rs:700] inlined_funcs_without_ranges = 0 185 | [src/bin/dwarf_exploration.rs:701] total_func_link_name_len = 73269012 186 | [src/bin/dwarf_exploration.rs:702] total_func_full_name_len = 25915453 187 | [src/bin/dwarf_exploration.rs:703] total_func_local_name_len = 24997566 188 | [src/bin/dwarf_exploration.rs:704] total_var_name_len = 127335519 189 | [src/bin/dwarf_exploration.rs:705] total_type_full_name_len = 278636616 190 | [src/bin/dwarf_exploration.rs:706] longest_origin_chain = 4 191 | [src/bin/dwarf_exploration.rs:707] vars_without_loc = 3384588 192 | [src/bin/dwarf_exploration.rs:708] func_instr_len = 175790195 193 | 194 | dwarf5-debug 195 | .eh_frame took 5.161s 196 | [src/bin/dwarf_exploration.rs:108] cies.len() = 3 197 | [src/bin/dwarf_exploration.rs:109] fde_count = 2653469 198 | [src/bin/dwarf_exploration.rs:110] row_count = 10931015 199 | [src/bin/dwarf_exploration.rs:111] bytes_covered_by_fdes = 452438729 200 | [src/bin/dwarf_exploration.rs:112] bytes_covered_by_rows = 452438729 201 | [src/bin/dwarf_exploration.rs:113] register_rules = 19295924 202 | [src/bin/dwarf_exploration.rs:114] max_rows_per_fde = 924 203 | ----- 204 | .debug_info pre-pass took 0.541s 205 | [src/bin/dwarf_exploration.rs:150] units_vec.len() = 8720 206 | [src/bin/dwarf_exploration.rs:151] line_programs.len() = 8720 207 | ----- 208 | .debug_info took 49.173s 209 | [src/bin/dwarf_exploration.rs:730] dies = 129375765 210 | [src/bin/dwarf_exploration.rs:731] attrs = 441002637 211 | [src/bin/dwarf_exploration.rs:732] funcs_including_decls_and_inline = 27152480 212 | [src/bin/dwarf_exploration.rs:733] funcs = 7175532 213 | [src/bin/dwarf_exploration.rs:734] deduped_funcs = 2654177 214 | [src/bin/dwarf_exploration.rs:735] template_funcs as f64 / funcs as f64 = 0.3630610246041687 215 | [src/bin/dwarf_exploration.rs:736] funcs_without_name as f64 / funcs as f64 = 0.0015041393446506824 216 | [src/bin/dwarf_exploration.rs:737] funcs_without_link_name as f64 / funcs as f64 = 0.00800219412302809 217 | [src/bin/dwarf_exploration.rs:738] funcs_with_multiple_ranges = 0 218 | [src/bin/dwarf_exploration.rs:739] func_ranges = 7175532 219 | [src/bin/dwarf_exploration.rs:740] vars = 20578836 220 | [src/bin/dwarf_exploration.rs:741] locs = 20828691 221 | [src/bin/dwarf_exploration.rs:742] types = 3800629 222 | [src/bin/dwarf_exploration.rs:743] template_types as f64 / types as f64 = 0.6688858607351572 223 | [src/bin/dwarf_exploration.rs:744] fields = 3374186 224 | [src/bin/dwarf_exploration.rs:745] inlined_funcs = 865915 225 | [src/bin/dwarf_exploration.rs:746] inlined_func_ranges = 1020198 226 | [src/bin/dwarf_exploration.rs:747] inlined_funcs_without_ranges = 0 227 | [src/bin/dwarf_exploration.rs:748] total_func_link_name_len = 970940656 228 | [src/bin/dwarf_exploration.rs:749] total_func_full_name_len = 473425651 229 | [src/bin/dwarf_exploration.rs:750] total_func_local_name_len = 459483480 230 | [src/bin/dwarf_exploration.rs:751] total_var_name_len = 126784836 231 | [src/bin/dwarf_exploration.rs:752] total_type_full_name_len = 499787777 232 | [src/bin/dwarf_exploration.rs:753] longest_origin_chain = 3 233 | [src/bin/dwarf_exploration.rs:754] vars_without_loc = 250023 234 | [src/bin/dwarf_exploration.rs:755] func_instr_len = 855639507 235 | ----- 236 | .debug_line took 4.499s 237 | [src/bin/dwarf_exploration.rs:972] num_empty_sequences = 0 238 | [src/bin/dwarf_exploration.rs:973] num_sequences_starting_at_zero = 4522910 239 | [src/bin/dwarf_exploration.rs:974] max_address_in_sequences_starting_at_zero = 52455 240 | [src/bin/dwarf_exploration.rs:975] num_sequences = 2090403 241 | [src/bin/dwarf_exploration.rs:976] num_all_rows = 84872875 242 | [src/bin/dwarf_exploration.rs:977] num_rows = 45021504 243 | [src/bin/dwarf_exploration.rs:978] num_include_directories = 29961 244 | [src/bin/dwarf_exploration.rs:979] num_files = 929705 245 | [src/bin/dwarf_exploration.rs:980] file_to_idx.len() = 27170 246 | [src/bin/dwarf_exploration.rs:981] kinda_num_statements = 34033305 247 | [src/bin/dwarf_exploration.rs:982] kinda_num_basic_blocks = 0 248 | [src/bin/dwarf_exploration.rs:983] kinda_num_prologues = 7162965 249 | [src/bin/dwarf_exploration.rs:984] kinda_num_epilogues = 7027519 250 | [src/bin/dwarf_exploration.rs:985] num_programs_where_files_have_md5 = 2096 251 | [src/bin/dwarf_exploration.rs:986] addr_bytes_covered = 460979119 252 | [src/bin/dwarf_exploration.rs:987] total_program_header_bytes = 5931269 253 | [src/bin/dwarf_exploration.rs:988] total_program_bytes = 465423316 254 | [src/bin/dwarf_exploration.rs:989] max_program_header_bytes = 7540 255 | [src/bin/dwarf_exploration.rs:990] max_program_bytes = 8293728 256 | [src/bin/dwarf_exploration.rs:991] max_include_directories = 71 257 | [src/bin/dwarf_exploration.rs:992] max_files = 623 258 | [src/bin/dwarf_exploration.rs:993] max_rows_per_program = 1651641 259 | [src/bin/dwarf_exploration.rs:994] max_rows_per_sequence = 146011 260 | [src/bin/dwarf_exploration.rs:995] duplicate_sequences = 0 261 | [src/bin/dwarf_exploration.rs:996] overlapping_sequences = 0 262 | [src/bin/dwarf_exploration.rs:997] deduped_addr_bytes_covered = 460979119 263 | [src/bin/dwarf_exploration.rs:998] mem::size_of::() = 64 264 | [src/bin/dwarf_exploration.rs:999] max_line = 333055 265 | [src/bin/dwarf_exploration.rs:1000] max_column = 408 266 | [src/bin/dwarf_exploration.rs:1005] all_anchors.len() = 74567209 267 | [src/bin/dwarf_exploration.rs:1008] all_anchors.len() = 55052832 268 | [src/bin/dwarf_exploration.rs:1025] longest_run_without_anchors = 30779 269 | sorting anchors took 2.879s 270 | 271 | dwz-dwarf4-official-release 272 | [src/bin/dwarf_exploration.rs:684] units = 92605 273 | [src/bin/dwarf_exploration.rs:685] dies = 59109648 274 | [src/bin/dwarf_exploration.rs:686] attrs = 197105241 275 | [src/bin/dwarf_exploration.rs:687] funcs_including_decls_and_inline = 7129631 276 | [src/bin/dwarf_exploration.rs:688] funcs = 316368 277 | [src/bin/dwarf_exploration.rs:689] deduped_funcs = 316355 278 | [src/bin/dwarf_exploration.rs:690] template_funcs as f64 / funcs as f64 = 0.08716115409902392 279 | [src/bin/dwarf_exploration.rs:691] funcs_without_name as f64 / funcs as f64 = 0.010901861123754615 280 | [src/bin/dwarf_exploration.rs:692] funcs_without_link_name as f64 / funcs as f64 = 0.06794935012390634 281 | [src/bin/dwarf_exploration.rs:693] funcs_with_multiple_ranges = 0 282 | [src/bin/dwarf_exploration.rs:694] func_ranges = 316368 283 | [src/bin/dwarf_exploration.rs:695] vars = 16021617 284 | [src/bin/dwarf_exploration.rs:696] locs = 21227504 285 | [src/bin/dwarf_exploration.rs:697] types = 1153774 286 | [src/bin/dwarf_exploration.rs:698] template_types as f64 / types as f64 = 0.6476155642266163 287 | [src/bin/dwarf_exploration.rs:699] fields = 1747701 288 | [src/bin/dwarf_exploration.rs:700] inlined_funcs = 9474795 289 | [src/bin/dwarf_exploration.rs:701] inlined_func_ranges = 12344834 290 | [src/bin/dwarf_exploration.rs:702] inlined_funcs_without_ranges = 0 291 | [src/bin/dwarf_exploration.rs:703] total_func_link_name_len = 40081317 292 | [src/bin/dwarf_exploration.rs:704] total_func_full_name_len = 12118588 293 | [src/bin/dwarf_exploration.rs:705] total_func_local_name_len = 11534211 294 | [src/bin/dwarf_exploration.rs:706] total_var_name_len = 97429214 295 | [src/bin/dwarf_exploration.rs:707] total_type_full_name_len = 168595996 296 | [src/bin/dwarf_exploration.rs:708] longest_origin_chain = 4 297 | [src/bin/dwarf_exploration.rs:709] vars_without_loc = 2374822 298 | [src/bin/dwarf_exploration.rs:710] func_instr_len = 133758066 299 | --------------------------------------------------------------------------------