1{ nixpkgs, pkgs }:
2
3let
4 inherit (pkgs) lib stdenvNoCC;
5 evalSystem = "x86_64-linux";
6in
7
8stdenvNoCC.mkDerivation {
9 name = "nixpkgs-metrics";
10
11 # Use structured attrs to pass in relevant information.
12 __structuredAttrs = true;
13 inherit evalSystem nixpkgs;
14
15 outputs = [
16 "out"
17 "raw"
18 ];
19
20 nativeBuildInputs = map lib.getBin [
21 pkgs.nixVersions.latest
22 pkgs.time
23 pkgs.jq
24 ];
25
26 # see https://github.com/NixOS/nixpkgs/issues/52436
27 #requiredSystemFeatures = [ "benchmark" ]; # dedicated `t2a` machine, by @vcunat
28
29 # Required because this derivation doesn't have a `src`.
30 dontUnpack = true;
31
32 configurePhase = ''
33 runHook preConfigure
34
35 export NIX_STORE_DIR=$TMPDIR/store
36 export NIX_STATE_DIR=$TMPDIR/state
37 export NIX_PAGER=
38 nix-store --init
39
40 runHook postConfigure
41 '';
42
43 buildPhase = ''
44 runHook preBuild
45
46 release="$nixpkgs/nixos/release.nix"
47
48 run() {
49 local name="$1"
50 shift
51
52 echo "running $@"
53
54 mkdir -p "metrics/$name"
55 local output="metrics/$name/output"
56 local nix_stats="metrics/$name/nix-stats.json"
57 local time_stats="metrics/$name/time-stats.json"
58
59 NIX_SHOW_STATS=1 NIX_SHOW_STATS_PATH="$nix_stats" command time -o "$time_stats" -- "$@" > "$output"
60
61 # Show the Nix statistics and the `time` statistics.
62 echo "Nix statistics for $@"
63 jq . "$nix_stats"
64 echo
65 echo "Time statistics for $@"
66 jq . "$time_stats"
67 echo
68
69 cpuTime="$(jq '.cpuTime' < "$nix_stats")"
70 [[ -n $cpuTime ]] || exit 1
71 echo "$name.time $cpuTime s" >> hydra-metrics
72
73 maxresident="$(jq '.max_resident_set_kb' < "$time_stats")"
74 [[ -n $maxresident ]] || exit 1
75 echo "$name.maxresident $maxresident KiB" >> hydra-metrics
76
77 # Nix also outputs `.symbols.bytes` but since that wasn't summed originally, we don't count it here.
78 allocations="$(jq '[.envs,.list,.values,.sets] | map(.bytes) | add' < "$nix_stats")"
79 [[ -n $allocations ]] || exit 1
80 echo "$name.allocations $allocations B" >> hydra-metrics
81
82 values="$(jq '.values.number' < "$nix_stats")"
83 [[ -n $values ]] || exit 1
84 echo "$name.values $values" >> hydra-metrics
85 }
86
87 run nixos.smallContainer nix-instantiate --option eval-system "$evalSystem" --dry-run "$release" -A closures.smallContainer.x86_64-linux --show-trace --no-gc-warning
88 run nixos.kde nix-instantiate --option eval-system "$evalSystem" --dry-run "$release" -A closures.kde.x86_64-linux --show-trace --no-gc-warning
89 run nixos.lapp nix-instantiate --option eval-system "$evalSystem" --dry-run "$release" -A closures.lapp.x86_64-linux --show-trace --no-gc-warning
90 run nix-env.qa nix-env --option eval-system "$evalSystem" -f "$nixpkgs" -qa
91 run nix-env.qaDrv nix-env --option eval-system "$evalSystem" -f "$nixpkgs" -qa --drv-path --meta --json
92
93 # It's slightly unclear which of the set to track: qaCount, qaCountDrv, qaCountBroken.
94 num="$(wc -l < metrics/nix-env.qa/output)"
95 echo "nix-env.qaCount $num" >> hydra-metrics
96 qaCountDrv="$(jq -r 'reduce (.[].drvPath? // empty) as $d (0; .+1)' metrics/nix-env.qaDrv/output)"
97 numBroken="$((num - $qaCountDrv))"
98 echo "nix-env.qaCountBroken $numBroken" >> hydra-metrics
99
100 lines="$(find "$nixpkgs" -name "*.nix" -type f -print0 | xargs -0 cat | wc -l)"
101 echo "loc $lines" >> hydra-metrics
102
103 runHook postBuild
104 '';
105
106 installPhase = ''
107 runHook preInstall
108
109 mkdir -p $out/nix-support
110 touch $out/nix-support/hydra-build-products
111 mv hydra-metrics $out/nix-support/hydra-metrics
112
113 # Save and compress the raw output
114 mv metrics $raw
115 xz -v $raw/*/output
116
117 runHook postInstall
118 '';
119
120 meta = {
121 description = "Metrics tracked by Hydra about Nixpkgs";
122 homepage = "https://hydra.nixos.org/job/nixpkgs/trunk/metrics";
123 longDescription = ''
124 View the metrics for Nixpkgs evaluation over time at these URLs.
125
126 These are all produced from running `nix` with `NIX_SHOW_STATS=1`.
127 See `EvalState::printStatistics` in the Nix source code for the implementation.
128 None of these metrics are inherently meaningful on their own.
129 Exercise caution in interpreting them as "bad" or "good".
130
131 # Total repository statistics
132
133 - [Lines of code in Nixpkgs](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/loc)
134 - [Count of broken packages using `nix-env -qa`](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaCountBroken)
135 - [Count of packages using `nix-env -qa`](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaCount)
136
137 # Statistics about representative commands
138
139 These are statistics gathered by running commands against Nixpkgs.
140
141 | Name | Command |
142 |------|---------|
143 | `nix-env.qaDrv` | `nix-env -f ${nixpkgs} -qa --drv-path --meta --json` |
144 | `nix-env.qa` | `nix-env -f ${nixpkgs} -qa` |
145 | `nixos.kde` | `nix-instantiate --dry-run ${nixpkgs}/nixos/release.nix -A closures.kde.x86_64-linux --show-trace` |
146 | `nixos.lapp` | `nix-instantiate --dry-run ${nixpkgs}/nixos/release.nix -A closures.lapp.x86_64-linux --show-trace` |
147 | `nixos.smallContainer` | `nix-instantiate --dry-run ${nixpkgs}/nixos/release.nix -A closures.smallContainer.x86_64-linux --show-trace`|
148
149 ## Allocations performed (in bytes)
150
151 This counts `envs.bytes`, `list.bytes`, `values.bytes`, and `sets.bytes` from the Nix statistics.
152
153 - [nix-env.qa.allocations](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qa.allocations)
154 - [nix-env.qaDrv.allocations](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaDrv.allocations)
155 - [nixos.kde.allocations](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.kde.allocations)
156 - [nixos.lapp.allocations](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.lapp.allocations)
157 - [nixos.smallContainer.allocations](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.allocations)
158
159 ## Maximum resident size (in number of KiB)
160
161 This counts `maxresident` KiB (`%M`) from the `time` command on Linux.
162
163 - [nix-env.qa.maxresident](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qa.maxresident)
164 - [nix-env.qaDrv.maxresident](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaDrv.maxresident)
165 - [nixos.kde.maxresident](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.kde.maxresident)
166 - [nixos.lapp.maxresident](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.lapp.maxresident)
167 - [nixos.smallContainer.maxresident](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.maxresident)
168
169 ## Time taken (in seconds)
170
171 This counts `cpuTime` as reported in the Nix statistics. On Linux, this resolves to [`getrusage(RUSAGE_SELF)`](https://man7.org/linux/man-pages/man2/getrusage.2.html).
172
173 - [nix-env.qa.time](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qa.time)
174 - [nix-env.qaDrv.time](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaDrv.time)
175 - [nixos.kde.time](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.kde.time)
176 - [nixos.lapp.time](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.lapp.time)
177 - [nixos.smallContainer.time](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.time)
178
179 ## Number of values
180
181 This counts the total number of values allocated in Nix (see `EvalState::allocValue` in the Nix source code).
182
183 - [nix-env.qa.values](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qa.values)
184 - [nix-env.qaDrv.values](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nix-env.qaDrv.values)
185 - [nixos.kde.values](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.kde.values)
186 - [nixos.lapp.values](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.lapp.values)
187 - [nixos.smallContainer.values](https://hydra.nixos.org/job/nixpkgs/trunk/metrics/metric/nixos.smallContainer.values)
188 '';
189 };
190
191 # Convince `time` to output in JSON
192 env.TIME = builtins.toJSON {
193 real_time = "%e";
194 user_time = "%U";
195 sys_time = "%S";
196 cpu_percent = "%P";
197 max_resident_set_kb = "%M";
198 avg_resident_set_kb = "%t";
199 avg_total_mem_kb = "%K";
200 avg_data_kb = "%D";
201 avg_stack_kb = "%p";
202 avg_unshared_data_kb = "%X";
203 avg_shared_text_kb = "%Z";
204 page_faults_major = "%F";
205 page_faults_minor = "%R";
206 swaps = "%W";
207 context_switches_voluntary = "%c";
208 context_switches_involuntary = "%w";
209 io_reads = "%I";
210 io_writes = "%O";
211 signals_received = "%k";
212 exit_status = "%x";
213 command = "%C";
214 };
215
216 # Don't allow aliases anywhere in Nixpkgs for the metrics.
217 env.NIXPKGS_CONFIG = builtins.toFile "nixpkgs-config.nix" ''
218 {
219 allowAliases = false;
220 }
221 '';
222}