lightswitch 0.6.0

CPU profiler as a library for Linux suitable for on-demand and continuous profiling
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs = {
        nixpkgs.follows = "nixpkgs";
      };
    };

    crane.url = "github:ipetkov/crane";
  };
  outputs = { self, nixpkgs, flake-utils, rust-overlay, crane }:
    flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ]
      (system:
        let
          overlays = [ (import rust-overlay) ];
          pkgs = import nixpkgs {
            inherit system overlays;
          };
          elfutils' = (pkgs.elfutils.override { enableDebuginfod = false; }).overrideAttrs (attrs: {
            doCheck = false;
            doInstallCheck = false;
            configureFlags = attrs.configureFlags ++ [ "--without-zstd" ];
            nativeBuildInputs = attrs.nativeBuildInputs ++ [ pkgs.pkg-config ];
            # Both libelf and zlib export a function with the same name, breaking
            # static builds. Use zlib's rather than libelf's.
            postInstall = (attrs.postInstall or "") + ''
              ar d $out/lib/libelf.a crc32.o
            '';
          });
          # Nix provided `clang` adds a few compilation options that can't be used for BPF.
          clang' = with pkgs; (
            pkgs.writeShellScriptBin "clang" ''
              if [[ "$@" =~ "-target bpf" ]]; then
                exec ${llvmPackages_21.clang-unwrapped}/bin/clang -I${llvmPackages_21.clang-unwrapped.lib}/lib/clang/21/include "$@"
              else
                exec ${llvmPackages_21.clang}/bin/clang "$@"
              fi
            ''
          );
          buildInputs = with pkgs; [
            clang'
            llvmPackages_21.libcxx
            llvmPackages_21.libclang
            llvmPackages_21.lld
            elfutils'
            zlib.static
            zlib.dev
            glibc
            glibc.static
          ];
          nativeBuildInputs = with pkgs; [
            pkg-config
          ];
          rust-toolchain = pkgs.rust-bin.nightly.latest.default;
          craneLib = (crane.mkLib nixpkgs.legacyPackages.${system}).overrideToolchain rust-toolchain;
          integration-tests-progs = import ./tests/testprogs/shell.nix { inherit pkgs system; };
          commonArgs = {
            src = ./.;
            doCheck = false;
            buildInputs = buildInputs;
            nativeBuildInputs = nativeBuildInputs;
            LIBCLANG_PATH = with pkgs; lib.makeLibraryPath [ llvmPackages_21.libclang ];
            LIBBPF_SYS_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ zlib.static elfutils' ];
          };
          lightswitch = craneLib.buildPackage (
            commonArgs // {
              cargoArtifacts = craneLib.buildDepsOnly commonArgs;
            }
          );
          nixos-vmtest = import ./nixos-vmtest.nix {
            inherit pkgs lightswitch;
          };
          nixos-vmtest-no-kvm = import ./nixos-vmtest.nix {
            inherit pkgs lightswitch;
            requireKvm = false;
            enableKasan = true;
            enableExtraAssertions = true;
          };
          nixos-vmtest-kasan-assertions = import ./nixos-vmtest.nix {
            inherit pkgs lightswitch;
            enableKasan = true;
            enableExtraAssertions = true;
          };
          mkNixosVmtestRunner =
            name: test:
            pkgs.writeShellApplication {
              inherit name;
              text = ''
                echo "NixOS VM test succeeded: ${test}"
              '';
            };
        in
        with pkgs;
        {
          formatter = pkgs.nixpkgs-fmt;
          checks = {
            nixos-vmtest = nixos-vmtest.test;
            nixos-vmtest-no-kvm = nixos-vmtest-no-kvm.test;
            nixos-vmtest-kasan-assertions = nixos-vmtest-kasan-assertions.test;
            kasan-assertions-kernel-config = nixos-vmtest-kasan-assertions.kernel.configfile;
          };
          packages = {
            default = lightswitch;
            container = pkgs.dockerTools.buildLayeredImage {
              name = "lightswitch";
              config = {
                Entrypoint = [ "${lightswitch}/bin/lightswitch" ];
                Env = [
                  "RUST_BACKTRACE=1"
                ];
              };
            };
            vmtest = (import ./vm.nix { inherit pkgs; }).run-vmtest lightswitch;
            nixos-vmtest = mkNixosVmtestRunner "nixos-vmtest" nixos-vmtest.test;
            nixos-vmtest-no-kvm = mkNixosVmtestRunner "nixos-vmtest-no-kvm" nixos-vmtest-no-kvm.test;
            nixos-vmtest-kasan-assertions = mkNixosVmtestRunner "nixos-vmtest-kasan-assertions" nixos-vmtest-kasan-assertions.test;
            nixos-vmtest-driver = nixos-vmtest.test.driver;
            nixos-vmtest-no-kvm-driver = nixos-vmtest-no-kvm.test.driver;
            nixos-vmtest-kasan-assertions-driver = nixos-vmtest-kasan-assertions.test.driver;
            kasan-assertions-kernel = nixos-vmtest-kasan-assertions.kernel;
            integration-tests-progs = integration-tests-progs.all-progs;
          };
          devShells.default = mkShell {
            nativeBuildInputs = nativeBuildInputs;
            buildInputs = buildInputs ++ [
              (rust-toolchain.override {
                extensions = [
                  "rust-src"
                  "rust-analyzer"
                  "llvm-tools-preview"
                ];
              })
              # Debugging tools
              strace
              gdb
              bpftools
              # Upload container image to registry
              skopeo
              # Cargo subcommand tools
              ## To upgrade deps
              cargo-edit
              ## Snapshot testing
              cargo-insta
              ## Remove unused deps
              cargo-shear
              ## Release to crates.io
              cargo-release
              ## Ensure all the crates can be built independently
              cargo-hack
              # Commented out because this is typically not cached and it's rarely used
              # ocamlPackages.magic-trace
              just
              ## Analise and deal with profiles in the pprof format
              pprof
              graphviz
              cargo-llvm-cov
            ];
            LIBCLANG_PATH = lib.makeLibraryPath [ llvmPackages_21.libclang ];
            LIBBPF_SYS_LIBRARY_PATH = lib.makeLibraryPath [ zlib.static elfutils' ];
            RUST_GDB = "${gdb}/bin/gdb";
          };

        }
      );
}