Build test goes silent on failure in external function call

HI! First post here…

I am playing around with linear algebra, trying to call the LAPACK library. I noticed a weird behavior which puzzles me – essentially, when the external call to the LAPACK library (technically to the LAPACKE C-interface) fails zig build test goes completely silent and also for the other, unrelated tests.

Here is my code (version without bug):

const std = @import("std");

const lapack = struct {
    const int = c_int;

    // ---------------------------------------------------------------------------- //
    //lapack_int LAPACKE_dgesv( int matrix_layout, lapack_int n, lapack_int nrhs,
    //                          double* a, lapack_int lda, lapack_int* ipiv,
    //                          double* b, lapack_int ldb );
    // ---------------------------------------------------------------------------- //
    extern "lapacke64" fn LAPACKE_dgesv(
        layout: c_int,
        n: int,
        nrhs: int,
        A: *f64,
        lda: int,
        ipiv: *int,
        B: *f64,
        ldb: int,
    ) int;

    const dgesv = LAPACKE_dgesv;

    const MatrixLayout = enum(c_int) {
        ROW_MAJOR = 101,
        COL_MAJOR = 102,
    };
};

pub fn solve_lin_system() void {
    const log = std.log.scoped(.solve_lin_system);

    // zig fmt: off
    var a = [_]f64 {
        1.0, 2.0, 0.0,
        0.0, 1.0, 3.0,
        4.0, 5.0, 1.0
    };
    var b = [_]f64 {
        5.0, 0.0, 10.0
    };
    var ipiv = [_]lapack.int { 0, 0, 0 };
    // zig fmt: on

    const a_ptr: *f64 = @ptrCast(&a);
    const b_ptr: *f64 = @ptrCast(&b);
    const ipiv_ptr: *lapack.int = @ptrCast(&ipiv);

    const info = lapack.dgesv(
        @intFromEnum(lapack.MatrixLayout.COL_MAJOR),
        3,
        1,
        a_ptr,
        3,
        ipiv_ptr,
        b_ptr,
        3,
    );

    log.warn("\n returned {}\n", .{info});
    log.warn("\n result {any}\n", .{b});
    const expected = [_]f64 {-11.0, 2.0, 4.0};
    log.warn("\n expected{any}\n", .{expected});
}


test "solve_lin_system" {
    solve_lin_system();
}

test "a different test "
{
    const log = std.log.scoped(.elsewhere);
    log.warn("A message!!", .{});
}

which works as expected and prints all messages with zig build test

Now if I change the last 3 to 1 in the lapack.dgesv call zig build test prints nothing!

My build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const mode_option = std.Build.StandardOptimizeOptionOptions {
        .preferred_optimize_mode = std.builtin.OptimizeMode.Debug,
    };
    const optimize = b.standardOptimizeOption(mode_option);

    const mod = b.addModule("main", .{
            .pic = true,
            .link_libc = true,
            .target = target,
            .optimize = optimize,
            .root_source_file = b.path("src/root.zig"),
    });
    mod.linkSystemLibrary("lapacke64", .{});

    const lib = b.addSharedLibrary(.{
        .name = "dgesv_test",
        .root_module = mod,
    });

    b.installArtifact(lib);

    const lib_unit_tests = b.addTest(.{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
    });
    lib_unit_tests.linkSystemLibrary("lapacke64");
    lib_unit_tests.linkLibC();

    const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_lib_unit_tests.step);
}

Any idea about what is going on here would be appreciated! Thanks!

I am using the latest version of zig (or close enough): 0.15.0-dev.11+5c57e90ff

It is hard to say what exactly causes such behavior. I had similar problem when my tests were writing to stdout. Zig test-runner uses sdtout to communicate with sub-processes running actual tests. When I made sure that I am not writing to stdout or reading from stdin tests behave normally. A cursory inspection of your code does not point to any apparent writings to stdout. Probably it is something else and your logger is writing to stderr.

Thanks for the response, I did not know that.

I think the issue boils down to invoking UB on the C end which then messes with the test output. It is a bit frustrating since it behaves differently with other execution commands – zig test and zig run main.zig both print something (depending on the compilation flags either the solution, ignoring the bug, or detect the error (with ReleaseSafe) printing the library error message), however I guess with UB involved I cannot have any expectations.

2 Likes