diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml index bd8044e580..812efb238d 100644 --- a/.github/workflows/shared.yml +++ b/.github/workflows/shared.yml @@ -56,6 +56,33 @@ jobs: # - uses: actions/checkout@v3 + - name: Set swap space + if: ${{ matrix.target == 'linux-x86_64' || matrix.target == 'linux-aarch64'}} + run: | + echo "Memory and swap:" + free -h + echo + swapon --show + echo + + export SWAP_FILE=$(swapon --show=NAME | tail -n 1) + if test -z "${SWAP_FILE}"; then + export SWAP_FILE=/swapfile + else + sudo swapoff -a + sudo rm "${SWAP_FILE}" + fi + sudo fallocate -l 10G "${SWAP_FILE}" + sudo chmod 600 "${SWAP_FILE}" + sudo mkswap "${SWAP_FILE}" + sudo swapon "${SWAP_FILE}" + + echo "Memory and swap:" + free -h + echo + swapon --show + echo + - name: chown /usr/local if: ${{ matrix.target == 'linux-x86_64' || matrix.target == 'linux-aarch64'}} run: | @@ -64,7 +91,7 @@ jobs: - name: Set up build cache uses: actions/cache@v3 with: - key: ${{ matrix.target }}-cache + key: ${{ matrix.target }}-cache-1 path: | # # Cache bazel path on Linux. ~/.cache/bazel/_bazel_$(whoami) diff --git a/BUILD.bazel b/BUILD.bazel index 3569ed78a2..730fe0ec81 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -84,8 +84,8 @@ string_flag( # Version flag for gcc. string_flag( name = "gcc_version", - # musl-cross-make uses `gcc-9.4.0` by default. - build_setting_default = "9.4.0", + # musl-cross-make uses `gcc-11.4.0` by default. + build_setting_default = "11.4.0", visibility = ["//visibility:public"], ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96d1076c88..e2738b8d0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -153,3 +153,21 @@ jump to definition, cross-references, hovering, symbol renaming, etc.): ```console bazel run //bazel:refresh_compile_commands ``` + +### Debugging + +Prefer GDB for interactive debugging. You need to compile with the symbol table: + +```bash +bazel build :urbit --compilation_mode=dbg +gdb --args ./bazel-bin/pkg/vere/urbit zod +``` + +In GDB, set the following, and any breakpoints (e.g. `break u3m_bail`): + +```gdb +set follow-fork-mode child +handle SIGSEGV nostop noprint +``` + +Then run the Urbit program as usual with `r`. diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index da0bb64568..c977752462 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -233,6 +233,24 @@ versioned_http_file( version = "255fb1ca8206072f1d09425f0db61ecfe7ff5b17", ) +versioned_http_archive( + name = "libbacktrace", + build_file = "//bazel/third_party/libbacktrace:libbacktrace.BUILD", + sha256 = "609c17352ec38eaf5ff6618fcbfb38cd8fa0e94a15a0d9aa259df514bbf47fcd", + url = "https://github.com/ianlancetaylor/libbacktrace/archive/{version}.tar.gz", + strip_prefix = "libbacktrace-{version}", + version = "4ead348bb45f753121ca0bd44170ff8352d4c514", +) + +versioned_http_archive( + name = "libunwind", + build_file = "//bazel/third_party/libunwind:libunwind.BUILD", + sha256 = "ddf0e32dd5fafe5283198d37e4bf9decf7ba1770b6e7e006c33e6df79e6a6157", + url = "https://github.com/libunwind/libunwind/releases/download/v1.8.1/libunwind-1.8.1.tar.gz", + strip_prefix = "libunwind-{version}", + version = "1.8.1", +) + versioned_http_archive( name = "lmdb", build_file = "//bazel/third_party/lmdb:lmdb.BUILD", diff --git a/bazel/common_settings.bzl b/bazel/common_settings.bzl index 68a2232cd1..9ec74beda5 100644 --- a/bazel/common_settings.bzl +++ b/bazel/common_settings.bzl @@ -13,20 +13,14 @@ string_flag = rule( def vere_library(copts = [], linkopts = [], **kwargs): native.cc_library( - copts = copts + select({ + copts = copts + [ + "-fno-omit-frame-pointer", + ] + select({ "//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."], - "//conditions:default": ["-O3"] + "//conditions:default": ["-O3", "-g"] }) + select({ "//:lto": ['-flto'], - "//:thinlto": ['-flto=thin'], "//conditions:default": [] - }) + select({ - # Don't include source level debug info on macOS. See - # https://github.com/urbit/urbit/issues/5561 and - # https://github.com/urbit/vere/issues/131. - "//:debug": [], - "@platforms//os:linux": ["-g"], - "//conditions:default": [], }), linkopts = linkopts + ['-g'] + select({ "//:lto": ['-flto'], @@ -38,17 +32,14 @@ def vere_library(copts = [], linkopts = [], **kwargs): def vere_binary(copts = [], linkopts = [], **kwargs): native.cc_binary( - copts = copts + select({ + copts = copts + [ + "-fno-omit-frame-pointer", + ] + select({ "//:debug": ["-O0", "-g3", "-DC3DBG", "-fdebug-compilation-dir=."], - "//conditions:default": ["-O3"] + "//conditions:default": ["-O3", "-g"] }) + select({ "//:lto": ['-flto'], - "//:thinlto": ['-flto=thin'], "//conditions:default": [] - }) + select({ - "//:debug": [], - "@platforms//os:linux": ["-g"], - "//conditions:default": [], }), linkopts = linkopts + ['-g'] + select({ "//:lto": ['-flto'], diff --git a/bazel/third_party/expat/expat.BUILD b/bazel/third_party/expat/expat.BUILD index 030f81ed78..1c0a6bc36d 100644 --- a/bazel/third_party/expat/expat.BUILD +++ b/bazel/third_party/expat/expat.BUILD @@ -11,6 +11,12 @@ configure_make( "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], "//conditions:default": ["--jobs=`nproc`"], }), + configure_options = [ + ] + select({ + "@//:linux_aarch64": ["--host=aarch64-linux-musl"], + "@//:linux_x86_64": ["--host=x86_64-linux-musl"], + "//conditions:default": [], + }), copts = ["-O3"], lib_source = ":all", out_static_libs = ["libexpat.a"], diff --git a/bazel/third_party/libbacktrace/BUILD.bazel b/bazel/third_party/libbacktrace/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/libbacktrace/libbacktrace.BUILD b/bazel/third_party/libbacktrace/libbacktrace.BUILD new file mode 100644 index 0000000000..ecb9ed9036 --- /dev/null +++ b/bazel/third_party/libbacktrace/libbacktrace.BUILD @@ -0,0 +1,25 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +configure_make( + name = "libbacktrace", + args = [ + ] + select({ + "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], + "//conditions:default": ["--jobs=`nproc`"], + }), + configure_options = [ + ] + select({ + "@//:linux_aarch64": ["--host=aarch64-linux-musl"], + "@//:linux_x86_64": ["--host=x86_64-linux-musl"], + "//conditions:default": [], + }), + copts = ["-O3"], + lib_source = ":all", + out_static_libs = ["libbacktrace.a"], + visibility = ["//visibility:public"], +) diff --git a/bazel/third_party/libunwind/BUILD.bazel b/bazel/third_party/libunwind/BUILD.bazel new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bazel/third_party/libunwind/libunwind.BUILD b/bazel/third_party/libunwind/libunwind.BUILD new file mode 100644 index 0000000000..3934b8f8b3 --- /dev/null +++ b/bazel/third_party/libunwind/libunwind.BUILD @@ -0,0 +1,25 @@ +load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +configure_make( + name = "libunwind", + args = select({ + "@platforms//os:macos": ["--jobs=`sysctl -n hw.logicalcpu`"], + "//conditions:default": ["--jobs=`nproc`"], + }), + configure_options = [ + "--enable-debug-frame", + ] + select({ + "@//:linux_aarch64": ["--host=aarch64-linux-musl"], + "@//:linux_x86_64": ["--host=x86_64-linux-musl"], + "//conditions:default": [], + }), + copts = ["-O3 -g"], + lib_source = ":all", + out_static_libs = ["libunwind.a"], + visibility = ["//visibility:public"], +) diff --git a/bazel/third_party/urcrypt/urcrypt.BUILD b/bazel/third_party/urcrypt/urcrypt.BUILD index 973830447d..c41d78f5b0 100644 --- a/bazel/third_party/urcrypt/urcrypt.BUILD +++ b/bazel/third_party/urcrypt/urcrypt.BUILD @@ -15,7 +15,7 @@ configure_make( copts = [ "-Wall", "-g", - "-O3", + "-O2", ], deps = [ "@aes_siv", diff --git a/bazel/toolchain/BUILD.bazel b/bazel/toolchain/BUILD.bazel index aaed5de087..38fa379197 100644 --- a/bazel/toolchain/BUILD.bazel +++ b/bazel/toolchain/BUILD.bazel @@ -335,9 +335,9 @@ toolchain( # so introduces a circular dependency during Bazel C/C++ toolchain resolution. # musl-cross-make builds musl-libc-compatible gcc toolchains from source. -_musl_cross_make_version = "fe915821b652a7fa37b34a596f47d8e20bc72338" +_musl_cross_make_version = "99f2cbc7e230f72bde3394be3ebd50497cb53e89" -_musl_cross_make_archive = "https://github.com/richfelker/musl-cross-make/archive/{}.tar.gz".format(_musl_cross_make_version) +_musl_cross_make_archive = "https://github.com/ripperi/musl-cross-make/archive/{}.tar.gz".format(_musl_cross_make_version) genrule( name = "install-aarch64-linux-musl-gcc", @@ -349,6 +349,8 @@ genrule( echo ' tar -xf {}.tar.gz' >> $@ echo ' archive=musl-cross-make-{}' >> $@ echo ' echo OUTPUT=$$aarch64_linux_musl_install > $$archive/config.mak' >> $@ + echo ' echo GCC_VER=11.4.0 >> $$archive/config.mak' >> $@ + echo ' echo MUSL_VER=1.2.5 >> $$archive/config.mak' >> $@ echo ' TARGET=aarch64-linux-musl make -s -C$$archive -j`nproc`' >> $@ echo ' sudo TARGET=aarch64-linux-musl make -s -C$$archive -j`nproc` install' >> $@ echo ' sudo chown --recursive $$USER $$aarch64_linux_musl_install' >> $@ @@ -382,6 +384,8 @@ genrule( echo ' tar -xf {}.tar.gz' >> $@ echo ' archive=musl-cross-make-{}' >> $@ echo ' echo OUTPUT=$$x86_64_linux_musl_install > $$archive/config.mak' >> $@ + echo ' echo GCC_VER=11.4.0 >> $$archive/config.mak' >> $@ + echo ' echo MUSL_VER=1.2.5 >> $$archive/config.mak' >> $@ echo ' TARGET=x86_64-linux-musl make -s -C$$archive -j`nproc`' >> $@ echo ' sudo TARGET=x86_64-linux-musl make -s -C$$archive -j`nproc` install' >> $@ echo ' sudo chown --recursive $$USER $$x86_64_linux_musl_install' >> $@ diff --git a/pkg/noun/BUILD.bazel b/pkg/noun/BUILD.bazel index 444bf0be37..76959d569d 100644 --- a/pkg/noun/BUILD.bazel +++ b/pkg/noun/BUILD.bazel @@ -34,6 +34,7 @@ vere_library( "//pkg/ent", "//pkg/ur", "@gmp", + "@libbacktrace", "@murmur3", "@openssl", "@pdjson", @@ -41,10 +42,14 @@ vere_library( "@softblas", "@softfloat", "@urcrypt", + "@whereami", "@zlib", ] + select({ "@platforms//os:macos": ["//pkg/noun/platform/darwin"], - "@platforms//os:linux": ["//pkg/noun/platform/linux"], + "@platforms//os:linux": [ + "//pkg/noun/platform/linux", + "@libunwind", + ], "//conditions:default": [], }), ) diff --git a/pkg/noun/jets/c/aor.c b/pkg/noun/jets/c/aor.c new file mode 100644 index 0000000000..248661e777 --- /dev/null +++ b/pkg/noun/jets/c/aor.c @@ -0,0 +1,69 @@ +/// @file + +#include "jets/q.h" +#include "jets/w.h" + +#include "noun.h" + + u3_noun + u3qc_aor(u3_noun a, + u3_noun b) + { + while ( 1 ) { + if ( c3y == u3r_sing(a, b) ) return c3y; + if ( c3n == u3ud(a) ) { + if ( c3y == u3ud(b) ) return c3n; + if ( c3y == u3r_sing(u3h(a), u3h(b)) ) { + a = u3t(a); + b = u3t(b); + } + else { + a = u3h(a); + b = u3h(b); + } + } + else { + if ( c3n == u3ud(b) ) return c3y; + { + c3_w len_a_w = u3r_met(3, a); + c3_w len_b_w = u3r_met(3, b);; + c3_y *buf_a_y, *buf_b_y; + c3_y cut_a_y, cut_b_y; + if ( c3y == u3a_is_cat(a) ) { + buf_a_y = (c3_y*)&a; + } + else { + u3a_atom* a_u = u3a_to_ptr(a); + buf_a_y = (c3_y*)(a_u->buf_w); + } + if ( c3y == u3a_is_cat(b) ) { + buf_b_y = (c3_y*)&b; + } + else { + u3a_atom* b_u = u3a_to_ptr(b); + buf_b_y = (c3_y*)(b_u->buf_w); + } + c3_w len_min_w = c3_min(len_a_w, len_b_w); + for (c3_w i_w = 0; i_w < len_min_w; i_w++) { + cut_a_y = buf_a_y[i_w]; + cut_b_y = buf_b_y[i_w]; + if ( cut_a_y != cut_b_y ) return __(cut_a_y < cut_b_y); + } + return __(len_a_w < len_b_w); + } + } + } + } + + u3_noun + u3wc_aor(u3_noun cor) + { + u3_noun a, b; + + if ( c3n == u3r_mean(cor, u3x_sam_2, &a, u3x_sam_3, &b, 0) ) { + return u3m_bail(c3__exit); + } else { + return u3qc_aor(a, b); + } + } + diff --git a/pkg/noun/jets/q.h b/pkg/noun/jets/q.h index 720cd8a2de..63b6bdd523 100644 --- a/pkg/noun/jets/q.h +++ b/pkg/noun/jets/q.h @@ -48,6 +48,7 @@ /** Tier 3. **/ + u3_noun u3qc_aor(u3_atom, u3_atom); u3_noun u3qc_bex(u3_atom); u3_noun u3qc_xeb(u3_atom); u3_noun u3qc_can(u3_atom, u3_noun); diff --git a/pkg/noun/jets/tree.c b/pkg/noun/jets/tree.c index f4bb68ae17..9a6a8460c0 100644 --- a/pkg/noun/jets/tree.c +++ b/pkg/noun/jets/tree.c @@ -2541,6 +2541,8 @@ static u3j_core _138_two__by_d[] = static u3j_harm _138_two_mate_a[] = {{".2", u3wb_mate, c3y}, {}}; +static u3j_harm _138_two_aor_a[] = {{".2", u3wc_aor, c3y}, {}}; + static u3j_core _138_two_d[] = { { "tri", 3, 0, _138_tri_d, no_hashes, _140_tri_ho }, @@ -2567,6 +2569,7 @@ static u3j_core _138_two_d[] = { "welp", 7, _140_two_welp_a, 0, no_hashes }, { "zing", 7, _140_two_zing_a, 0, no_hashes }, + { "aor", 7, _138_two_aor_a, 0, no_hashes }, { "bex", 7, _140_two_bex_a, 0, no_hashes }, { "cat", 7, _140_two_cat_a, 0, no_hashes }, { "can", 7, _140_two_can_a, 0, no_hashes }, diff --git a/pkg/noun/jets/w.h b/pkg/noun/jets/w.h index 379723d0a9..ec8daa32ed 100644 --- a/pkg/noun/jets/w.h +++ b/pkg/noun/jets/w.h @@ -50,6 +50,7 @@ /** Tier 3. **/ + u3_noun u3wc_aor(u3_noun); u3_noun u3wc_bex(u3_noun); u3_noun u3wc_xeb(u3_noun); u3_noun u3wc_can(u3_noun); diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index 1ff748d528..c218b5311e 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -5,11 +5,20 @@ #include "pkg/noun/v3/manage.h" #include +#include #include +#if defined(U3_OS_osx) +#include +#endif #include #include +#if defined(U3_OS_linux) +#define UNW_LOCAL_ONLY +#include +#endif #include "allocate.h" +#include "backtrace.h" #include "events.h" #include "hashtable.h" #include "imprison.h" @@ -24,6 +33,7 @@ #include "trace.h" #include "urcrypt.h" #include "vortex.h" +#include "whereami.h" #include "xtract.h" // XX stack-overflow recovery should be gated by -a @@ -730,6 +740,164 @@ u3m_dump(void) } #endif +struct bt_cb_data { + c3_y count; + c3_y fail; + c3_c* pn_c; +}; + +static void +err_cb(void* data, const char* msg, int errnum) +{ + struct bt_cb_data* bdata = (struct bt_cb_data *)data; + bdata->count++; + + if ( bdata->count <= 1 ) { + /* u3l_log("Backtrace error %d: %s", errnum, msg); */ + bdata->fail = 1; + } +} + +static int +bt_cb(void* data, + uintptr_t pc, + const char* filename, + int lineno, + const char* function) +{ + struct bt_cb_data* bdata = (struct bt_cb_data *)data; + bdata->count++; + + Dl_info info = {}; + c3_c* fname_c = {0}; + + if ( dladdr((void *)pc, &info) ) { + for ( c3_w i_w = 0; info.dli_fname[i_w] != 0; i_w++ ) + if ( info.dli_fname[i_w] == '/' ) { + fname_c = (c3_c*)&info.dli_fname[i_w + 1]; + } + } + + if ( bdata->count <= 100 ) { + c3_c* loc[128]; + if (filename != 0) { + snprintf((c3_c*)loc, 128, "%s:%d", filename, lineno); + } + else { + snprintf((c3_c*)loc, 128, "%s", fname_c != 0 ? fname_c : "-"); + } + + c3_c* fn_c; + if (function != 0 || bdata->pn_c != 0) { + fn_c = (c3_c*)(function != 0 ? function : bdata->pn_c); + } + else { + fn_c = (c3_c*)(info.dli_sname != 0 ? info.dli_sname : "-"); + } + + fprintf(stderr, "%-3d %-35s %s\r\n", bdata->count - 1, fn_c, (c3_c *)loc); + + bdata->pn_c = 0; + return 0; + } + else { + bdata->pn_c = 0; + return 1; + } +} + +/* _self_path(): get binary self-path. + */ +static c3_y +_self_path(c3_c *pat_c) +{ + c3_i len_i = 0; + c3_i pat_i; + + if ( 0 < (len_i = wai_getExecutablePath(NULL, 0, &pat_i)) ) { + wai_getExecutablePath(pat_c, len_i, &pat_i); + pat_c[len_i] = 0; + return 0; + } + + return 1; +} + +void +u3m_stacktrace() +{ + void* bt_state; + c3_i ret_i; + struct bt_cb_data data = { 0, 0, 0 }; + c3_c* self_path_c[4096] = {0}; + +#if defined(U3_OS_osx) + fprintf(stderr, "Stacktrace:\r\n"); + + if ( _self_path((c3_c*)self_path_c) == 0 ) { + bt_state = backtrace_create_state((const c3_c*)self_path_c, 0, err_cb, 0); + ret_i = backtrace_full(bt_state, 0, bt_cb, err_cb, &data); + if (data.fail == 0) u3l_log(""); + } + else { + data.fail = 1; + } + + if ( data.fail == 1 ) { + void* array[100]; + c3_c** strings; + size_t size = backtrace(array, 100); + + strings = backtrace_symbols(array, size); + + if ( strings[0] == NULL ) { + fprintf(stderr, "Backtrace failed\r\n"); + } + else { + for ( c3_i i = 0; i < size; i++ ) { + fprintf(stderr, "%s\r\n", strings[i]); + } + u3l_log(""); + } + + free(strings); + } +#elif defined(U3_OS_linux) + /* TODO: Fix unwind not getting past signal trampoline on linux aarch64 + */ + fprintf(stderr, "Stacktrace:\r\n"); + + if ( _self_path((c3_c*)self_path_c) == 0 ) { + bt_state = backtrace_create_state((const c3_c*)self_path_c, 0, err_cb, 0); + + unw_context_t context; + unw_cursor_t cursor; + unw_getcontext(&context); + unw_init_local(&cursor, &context); + unw_word_t pc, sp; + + c3_c* pn_c[1024] = {0}; + c3_w offp_w = 0; + + do { + unw_get_reg(&cursor, UNW_REG_IP, &pc); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + if ( 0 == unw_get_proc_name(&cursor, pn_c, 1024, &offp_w) ) + data.pn_c = pn_c; + ret_i = backtrace_pcinfo(bt_state, pc - 1, bt_cb, err_cb, &data); + } while (unw_step(&cursor) > 0); + + if ( (data.count > 0) ) { + u3l_log(""); + } + } + else { + data.fail = 1; + fprintf(stderr, "Backtrace failed\r\n"); + } +#endif +} + /* u3m_bail(): bail out. Does not return. ** ** Bail motes: @@ -782,7 +950,8 @@ u3m_bail(u3_noun how) if ( &(u3H->rod_u) == u3R ) { // XX set exit code // - fprintf(stderr, "home: bailing out\r\n"); + fprintf(stderr, "home: bailing out\r\n\r\n"); + u3m_stacktrace(); abort(); } @@ -793,7 +962,8 @@ u3m_bail(u3_noun how) case c3__oops: { // XX set exit code // - fprintf(stderr, "bailing out\r\n"); + fprintf(stderr, "bailing out\r\n\r\n"); + u3m_stacktrace(); abort(); } } @@ -1773,7 +1943,7 @@ _cm_limits(void) } /* u3m_fault(): handle a memory event with libsigsegv protocol. -*/ + */ c3_i u3m_fault(void* adr_v, c3_i ser_i) { @@ -1789,7 +1959,8 @@ u3m_fault(void* adr_v, c3_i ser_i) // else if ( (adr_w < u3_Loom) || (adr_w >= (u3_Loom + u3C.wor_i)) ) { fprintf(stderr, "loom: external fault: %p (%p : %p)\r\n\r\n", - adr_w, u3_Loom, u3_Loom + u3C.wor_i); + adr_w, u3_Loom, u3_Loom + u3C.wor_i); + u3m_stacktrace(); u3_assert(0); return 0; } diff --git a/pkg/vere/king.c b/pkg/vere/king.c index 917c862036..285ac18a49 100644 --- a/pkg/vere/king.c +++ b/pkg/vere/king.c @@ -1441,7 +1441,7 @@ _king_copy_file(c3_c* src_c, c3_c* dst_c) do { // XX fallback on any errors? // - if ( 0 > (sen_i = sendfile64(dst_i, src_i, &off_i, len_i)) ) { + if ( 0 > (sen_i = sendfile(dst_i, src_i, &off_i, len_i)) ) { err_i = errno; ret_i = -1; goto done3;