Bug Summary

File:root/firefox-clang/tools/power/rapl.cpp
Warning:line 515, column 17
Assigned value is uninitialized

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name rapl.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/tools/power -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/tools/power -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -I /root/firefox-clang/tools/power -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/tools/power -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/tools/power/rapl.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// This program provides processor power estimates. It does this by reading
6// model-specific registers (MSRs) that are part Intel's Running Average Power
7// Limit (RAPL) interface. These MSRs provide good quality estimates of the
8// energy consumption of up to four system components:
9// - PKG: the entire processor package;
10// - PP0: the cores (a subset of the package);
11// - PP1: the GPU (a subset of the package);
12// - DRAM: main memory.
13//
14// For more details about RAPL, see section 14.9 of Volume 3 of the "Intel 64
15// and IA-32 Architecture's Software Developer's Manual", Order Number 325384.
16//
17// This program exists because there are no existing tools on Mac that can
18// obtain all four RAPL estimates. (|powermetrics| can obtain the package
19// estimate, but not the others. Intel Power Gadget can obtain the package and
20// cores estimates.)
21//
22// On Linux |perf| can obtain all four estimates (as Joules, which are easily
23// converted to Watts), but this program is implemented for Linux because it's
24// not too hard to do, and that gives us multi-platform consistency.
25//
26// This program does not support Windows, unfortunately. It's not obvious how
27// to access the RAPL MSRs on Windows.
28//
29// This program deliberately uses only standard libraries and avoids
30// Mozilla-specific code, to make it easy to compile and test on different
31// machines.
32
33#include <assert.h>
34#include <getopt.h>
35#include <math.h>
36#include <signal.h>
37#include <stdarg.h>
38#include <stdint.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/time.h>
43#include <unistd.h>
44
45#include <algorithm>
46#include <numeric>
47#include <vector>
48
49#if defined(MOZ_CLANG_PLUGIN) && defined(__GLIBCXX__20260809) && \
50 (__GLIBCXX__20260809 <= 20230707)
51# define MOZ_GLIBCXX_CONSTINIT __attribute__((annotate("moz_global_var")))
52#else
53# define MOZ_GLIBCXX_CONSTINIT
54#endif
55
56//---------------------------------------------------------------------------
57// Utilities
58//---------------------------------------------------------------------------
59
60// The value of argv[0] passed to main(). Used in error messages.
61static const char* gArgv0;
62
63static void Abort(const char* aFormat, ...) {
64 va_list vargs;
65 va_start(vargs, aFormat)__builtin_va_start(vargs, aFormat);
66 fprintf(stderrstderr, "%s: ", gArgv0);
67 vfprintf(stderrstderr, aFormat, vargs);
68 fprintf(stderrstderr, "\n");
69 va_end(vargs)__builtin_va_end(vargs);
70
71 exit(1);
72}
73
74static void CmdLineAbort(const char* aMsg) {
75 if (aMsg) {
76 fprintf(stderrstderr, "%s: %s\n", gArgv0, aMsg);
77 }
78 fprintf(stderrstderr, "Use --help for more information.\n");
79 exit(1);
80}
81
82// A special value that represents an estimate from an unsupported RAPL domain.
83static const double kUnsupported_j = -1.0;
84
85// Print to stdout and flush it, so that the output appears immediately even if
86// being redirected through |tee| or anything like that.
87static void PrintAndFlush(const char* aFormat, ...) {
88 va_list vargs;
89 va_start(vargs, aFormat)__builtin_va_start(vargs, aFormat);
90 vfprintf(stdoutstdout, aFormat, vargs);
91 va_end(vargs)__builtin_va_end(vargs);
92
93 fflush(stdoutstdout);
94}
95
96//---------------------------------------------------------------------------
97// Mac-specific code
98//---------------------------------------------------------------------------
99
100#if defined(__APPLE__)
101
102// Because of the pkg_energy_statistics_t::pkes_version check below, the
103// earliest OS X version this code will work with is 10.9.0 (xnu-2422.1.72).
104
105# include <sys/types.h>
106# include <sys/sysctl.h>
107
108// OS X has four kinds of system calls:
109//
110// 1. Mach traps;
111// 2. UNIX system calls;
112// 3. machine-dependent calls;
113// 4. diagnostic calls.
114//
115// (See "Mac OS X and iOS Internals" by Jonathan Levin for more details.)
116//
117// The last category has a single call named diagCall() or diagCall64(). Its
118// mode is controlled by its first argument, and one of the modes allows access
119// to the Intel RAPL MSRs.
120//
121// The interface to diagCall64() is not exported, so we have to import some
122// definitions from the XNU kernel. All imported definitions are annotated with
123// the XNU source file they come from, and information about what XNU versions
124// they were introduced in and (if relevant) modified.
125
126// The diagCall64() mode.
127// From osfmk/i386/Diagnostics.h
128// - In 10.8.4 (xnu-2050.24.15) this value was introduced. (In 10.8.3 the value
129// 17 was used for dgGzallocTest.)
130# define dgPowerStat 17
131
132// From osfmk/i386/cpu_data.h
133// - In 10.8.5 these values were introduced, along with core_energy_stat_t.
134# define CPU_RTIME_BINS (12)
135# define CPU_ITIME_BINS (CPU_RTIME_BINS)
136
137// core_energy_stat_t and pkg_energy_statistics_t are both from
138// osfmk/i386/Diagnostics.c.
139// - In 10.8.4 (xnu-2050.24.15) both structs were introduced, but with many
140// fewer fields.
141// - In 10.8.5 (xnu-2050.48.11) both structs were substantially expanded, with
142// numerous new fields.
143// - In 10.9.0 (xnu-2422.1.72) pkg_energy_statistics_t::pkes_version was added.
144// diagCall64(dgPowerStat) fills it with '1' in all versions since (up to
145// 10.10.2 at time of writing).
146// - in 10.10.2 (xnu-2782.10.72) core_energy_stat_t::gpmcs was conditionally
147// added, if DIAG_ALL_PMCS is true. (DIAG_ALL_PMCS is not even defined in the
148// source code, but it could be defined at compile-time via compiler flags.)
149// pkg_energy_statistics_t::pkes_version did not change, though.
150
151typedef struct {
152 uint64_t caperf;
153 uint64_t cmperf;
154 uint64_t ccres[6];
155 uint64_t crtimes[CPU_RTIME_BINS];
156 uint64_t citimes[CPU_ITIME_BINS];
157 uint64_t crtime_total;
158 uint64_t citime_total;
159 uint64_t cpu_idle_exits;
160 uint64_t cpu_insns;
161 uint64_t cpu_ucc;
162 uint64_t cpu_urc;
163# if DIAG_ALL_PMCS // Added in 10.10.2 (xnu-2782.10.72).
164 uint64_t gpmcs[4]; // Added in 10.10.2 (xnu-2782.10.72).
165# endif /* DIAG_ALL_PMCS */ // Added in 10.10.2 (xnu-2782.10.72).
166} core_energy_stat_t;
167
168typedef struct {
169 uint64_t pkes_version; // Added in 10.9.0 (xnu-2422.1.72).
170 uint64_t pkg_cres[2][7];
171
172 // This is read from MSR 0x606, which Intel calls MSR_RAPL_POWER_UNIT
173 // and XNU calls MSR_IA32_PKG_POWER_SKU_UNIT.
174 uint64_t pkg_power_unit;
175
176 // These are the four fields for the four RAPL domains. For each field
177 // we list:
178 //
179 // - the corresponding MSR number;
180 // - Intel's name for that MSR;
181 // - XNU's name for that MSR;
182 // - which Intel processors the MSR is supported on.
183 //
184 // The last of these is determined from chapter 35 of Volume 3 of the
185 // "Intel 64 and IA-32 Architecture's Software Developer's Manual",
186 // Order Number 325384. (Note that chapter 35 contradicts section 14.9
187 // to some degree.)
188
189 // 0x611 == MSR_PKG_ENERGY_STATUS == MSR_IA32_PKG_ENERGY_STATUS
190 // Atom (various), Sandy Bridge, Next Gen Xeon Phi (model 0x57).
191 uint64_t pkg_energy;
192
193 // 0x639 == MSR_PP0_ENERGY_STATUS == MSR_IA32_PP0_ENERGY_STATUS
194 // Atom (various), Sandy Bridge, Next Gen Xeon Phi (model 0x57).
195 uint64_t pp0_energy;
196
197 // 0x641 == MSR_PP1_ENERGY_STATUS == MSR_PP1_ENERGY_STATUS
198 // Sandy Bridge, Haswell.
199 uint64_t pp1_energy;
200
201 // 0x619 == MSR_DRAM_ENERGY_STATUS == MSR_IA32_DDR_ENERGY_STATUS
202 // Xeon E5, Xeon E5 v2, Haswell/Haswell-E, Next Gen Xeon Phi (model
203 // 0x57)
204 uint64_t ddr_energy;
205
206 uint64_t llc_flushed_cycles;
207 uint64_t ring_ratio_instantaneous;
208 uint64_t IA_frequency_clipping_cause;
209 uint64_t GT_frequency_clipping_cause;
210 uint64_t pkg_idle_exits;
211 uint64_t pkg_rtimes[CPU_RTIME_BINS];
212 uint64_t pkg_itimes[CPU_ITIME_BINS];
213 uint64_t mbus_delay_time;
214 uint64_t mint_delay_time;
215 uint32_t ncpus;
216 core_energy_stat_t cest[];
217} pkg_energy_statistics_t;
218
219static int diagCall64(uint64_t aMode, void* aBuf) {
220 // We cannot use syscall() here because it doesn't work with diagnostic
221 // system calls -- it raises SIGSYS if you try. So we have to use asm.
222
223# ifdef __x86_64__1
224 // The 0x40000 prefix indicates it's a diagnostic system call. The 0x01
225 // suffix indicates the syscall number is 1, which also happens to be the
226 // only diagnostic system call. See osfmk/mach/i386/syscall_sw.h for more
227 // details.
228 static const uint64_t diagCallNum = 0x4000001;
229 uint64_t rv;
230
231 __asm__ __volatile__(
232 "syscall"
233
234 // Return value goes in "a" (%rax).
235 : /* outputs */ "=a"(rv)
236
237 // The syscall number goes in "0", a synonym (from outputs) for "a"
238 // (%rax). The syscall arguments go in "D" (%rdi) and "S" (%rsi).
239 : /* inputs */ "0"(diagCallNum), "D"(aMode), "S"(aBuf)
240
241 // The |syscall| instruction clobbers %rcx, %r11, and %rflags ("cc"). And
242 // this particular syscall also writes memory (aBuf).
243 : /* clobbers */ "rcx", "r11", "cc", "memory");
244 return rv;
245# else
246# error Sorry, only x86-64 is supported
247# endif
248}
249
250static void diagCall64_dgPowerStat(pkg_energy_statistics_t* aPkes) {
251 static const uint64_t supported_version = 1;
252
253 // Write an unsupported version number into pkes_version so that the check
254 // below cannot succeed by dumb luck.
255 aPkes->pkes_version = supported_version - 1;
256
257 // diagCall64() returns 1 on success, and 0 on failure (which can only happen
258 // if the mode is unrecognized, e.g. in 10.7.x or earlier versions).
259 if (diagCall64(dgPowerStat, aPkes) != 1) {
260 Abort("diagCall64() failed");
261 }
262
263 if (aPkes->pkes_version != 1) {
264 Abort("unexpected pkes_version: %llu", aPkes->pkes_version);
265 }
266}
267
268class RAPL {
269 bool mIsGpuSupported; // Is the GPU domain supported by the processor?
270 bool mIsRamSupported; // Is the RAM domain supported by the processor?
271
272 // The DRAM domain on Haswell servers has a fixed energy unit (1/65536 J ==
273 // 15.3 microJoules) which is different to the power unit MSR. (See the
274 // "Intel Xeon Processor E5-1600 and E5-2600 v3 Product Families, Volume 2 of
275 // 2, Registers" datasheet, September 2014, Reference Number: 330784-001.)
276 // This field records whether the quirk is present.
277 bool mHasRamUnitsQuirk;
278
279 // The abovementioned 15.3 microJoules value.
280 static const double kQuirkyRamJoulesPerTick;
281
282 // The previous sample's MSR values.
283 uint64_t mPrevPkgTicks;
284 uint64_t mPrevPp0Ticks;
285 uint64_t mPrevPp1Ticks;
286 uint64_t mPrevDdrTicks;
287
288 // The struct passed to diagCall64().
289 pkg_energy_statistics_t* mPkes;
290
291 public:
292 RAPL() : mHasRamUnitsQuirk(false) {
293 // Work out which RAPL MSRs this CPU model supports.
294 int cpuModel;
295 size_t size = sizeof(cpuModel);
296 if (sysctlbyname("machdep.cpu.model", &cpuModel, &size, nullptr, 0) != 0) {
297 Abort("sysctlbyname(\"machdep.cpu.model\") failed");
298 }
299
300 // This is similar to arch/x86/kernel/cpu/perf_event_intel_rapl.c in
301 // linux-4.1.5/.
302 //
303 // By linux-5.6.14/, this stuff had moved into
304 // arch/x86/events/intel/rapl.c, which references processor families in
305 // arch/x86/include/asm/intel-family.h.
306 switch (cpuModel) {
307 case 0x2a: // Sandy Bridge
308 case 0x3a: // Ivy Bridge
309 // Supports package, cores, GPU.
310 mIsGpuSupported = true;
311 mIsRamSupported = false;
312 break;
313
314 case 0x3f: // Haswell X
315 case 0x4f: // Broadwell X
316 case 0x55: // Skylake X
317 case 0x56: // Broadwell D
318 // Supports package, cores, RAM. Has the units quirk.
319 mIsGpuSupported = false;
320 mIsRamSupported = true;
321 mHasRamUnitsQuirk = true;
322 break;
323
324 case 0x2d: // Sandy Bridge X
325 case 0x3e: // Ivy Bridge X
326 // Supports package, cores, RAM.
327 mIsGpuSupported = false;
328 mIsRamSupported = true;
329 break;
330
331 case 0x3c: // Haswell
332 case 0x3d: // Broadwell
333 case 0x45: // Haswell L
334 case 0x46: // Haswell G
335 case 0x47: // Broadwell G
336 // Supports package, cores, GPU, RAM.
337 mIsGpuSupported = true;
338 mIsRamSupported = true;
339 break;
340
341 case 0x4e: // Skylake L
342 case 0x5e: // Skylake
343 case 0x8e: // Kaby Lake L
344 case 0x9e: // Kaby Lake
345 case 0x66: // Cannon Lake L
346 case 0x7d: // Ice Lake
347 case 0x7e: // Ice Lake L
348 case 0xa5: // Comet Lake
349 case 0xa6: // Comet Lake L
350 // Supports package, cores, GPU, RAM, PSYS.
351 // XXX: this tool currently doesn't measure PSYS.
352 mIsGpuSupported = true;
353 mIsRamSupported = true;
354 break;
355
356 default:
357 Abort("unknown CPU model: %d", cpuModel);
358 break;
359 }
360
361 // Get the maximum number of logical CPUs so that we know how big to make
362 // |mPkes|.
363 int logicalcpu_max;
364 size = sizeof(logicalcpu_max);
365 if (sysctlbyname("hw.logicalcpu_max", &logicalcpu_max, &size, nullptr, 0) !=
366 0) {
367 Abort("sysctlbyname(\"hw.logicalcpu_max\") failed");
368 }
369
370 // Over-allocate by 1024 bytes per CPU to allow for the uncertainty around
371 // core_energy_stat_t::gpmcs and for any other future extensions to that
372 // struct. (The fields we read all come before the core_energy_stat_t
373 // array, so it won't matter to us whether gpmcs is present or not.)
374 size_t pkesSize = sizeof(pkg_energy_statistics_t) +
375 logicalcpu_max * sizeof(core_energy_stat_t) +
376 logicalcpu_max * 1024;
377 mPkes = (pkg_energy_statistics_t*)malloc(pkesSize);
378 if (!mPkes) {
379 Abort("malloc() failed");
380 }
381
382 // Do an initial measurement so that the first sample's diffs are sensible.
383 double dummy1, dummy2, dummy3, dummy4;
384 EnergyEstimates(dummy1, dummy2, dummy3, dummy4);
385 }
386
387 ~RAPL() { free(mPkes); }
388
389 static double Joules(uint64_t aTicks, double aJoulesPerTick) {
390 return double(aTicks) * aJoulesPerTick;
391 }
392
393 void EnergyEstimates(double& aPkg_J, double& aCores_J, double& aGpu_J,
394 double& aRam_J) {
395 diagCall64_dgPowerStat(mPkes);
396
397 // Bits 12:8 are the ESU.
398 // Energy measurements come in multiples of 1/(2^ESU).
399 uint32_t energyStatusUnits = (mPkes->pkg_power_unit >> 8) & 0x1f;
400 double joulesPerTick = ((double)1 / (1 << energyStatusUnits));
401
402 aPkg_J = Joules(mPkes->pkg_energy - mPrevPkgTicks, joulesPerTick);
403 aCores_J = Joules(mPkes->pp0_energy - mPrevPp0Ticks, joulesPerTick);
404 aGpu_J = mIsGpuSupported
405 ? Joules(mPkes->pp1_energy - mPrevPp1Ticks, joulesPerTick)
406 : kUnsupported_j;
407 aRam_J = mIsRamSupported
408 ? Joules(mPkes->ddr_energy - mPrevDdrTicks,
409 mHasRamUnitsQuirk ? kQuirkyRamJoulesPerTick
410 : joulesPerTick)
411 : kUnsupported_j;
412
413 mPrevPkgTicks = mPkes->pkg_energy;
414 mPrevPp0Ticks = mPkes->pp0_energy;
415 if (mIsGpuSupported) {
416 mPrevPp1Ticks = mPkes->pp1_energy;
417 }
418 if (mIsRamSupported) {
419 mPrevDdrTicks = mPkes->ddr_energy;
420 }
421 }
422};
423
424/* static */ const double RAPL::kQuirkyRamJoulesPerTick = (double)1 / 65536;
425
426//---------------------------------------------------------------------------
427// Linux-specific code
428//---------------------------------------------------------------------------
429
430#elif defined(__linux__1)
431
432# include <linux1/perf_event.h>
433# include <sys/syscall.h>
434
435// There is no glibc wrapper for this system call so we provide our own.
436static int perf_event_open(struct perf_event_attr* aAttr, pid_t aPid, int aCpu,
437 int aGroupFd, unsigned long aFlags) {
438 return syscall(__NR_perf_event_open298, aAttr, aPid, aCpu, aGroupFd, aFlags);
439}
440
441// Returns false if the file cannot be opened.
442template <typename T>
443static bool ReadValueFromPowerFile(const char* aStr1, const char* aStr2,
444 const char* aStr3, const char* aScanfString,
445 T* aOut) {
446 // The filenames going into this buffer are under our control and the longest
447 // one is "/sys/bus/event_source/devices/power/events/energy-cores.scale".
448 // So 256 chars is plenty.
449 char filename[256];
450
451 sprintf(filename, "/sys/bus/event_source/devices/power/%s%s%s", aStr1, aStr2,
452 aStr3);
453 FILE* fp = fopen(filename, "r");
454 if (!fp
8.1
'fp' is non-null
) {
9
Taking false branch
455 return false;
456 }
457 if (fscanf(fp, aScanfString, aOut) != 1) {
10
Taking true branch
458 Abort("fscanf() failed");
459 }
460 fclose(fp);
461
462 return true;
11
Returning without writing to '*aOut'
463}
464
465// This class encapsulates the reading of a single RAPL domain.
466class Domain {
467 bool mIsSupported; // Is the domain supported by the processor?
468
469 // These three are only set if |mIsSupported| is true.
470 double mJoulesPerTick; // How many Joules each tick of the MSR represents.
471 int mFd; // The fd through which the MSR is read.
472 double mPrevTicks; // The previous sample's MSR value.
473
474 public:
475 enum IsOptional { Optional, NonOptional };
476
477 Domain(const char* aName, uint32_t aType,
478 IsOptional aOptional = NonOptional) {
479 uint64_t config;
7
'config' declared without an initial value
480 if (!ReadValueFromPowerFile("events/energy-", aName, "", "event=%llx",
8
Calling 'ReadValueFromPowerFile<unsigned long>'
12
Returning from 'ReadValueFromPowerFile<unsigned long>'
13
Taking false branch
481 &config)) {
482 // Failure is allowed for optional domains.
483 if (aOptional == NonOptional) {
484 Abort(
485 "failed to open file for non-optional domain '%s'\n"
486 "- Is your kernel version 3.14 or later, as required? "
487 "Run |uname -r| to see.",
488 aName);
489 }
490 mIsSupported = false;
491 return;
492 }
493
494 mIsSupported = true;
495
496 if (!ReadValueFromPowerFile("events/energy-", aName, ".scale", "%lf",
14
Taking true branch
497 &mJoulesPerTick)) {
498 Abort("failed to read from .scale file");
499 }
500
501 // The unit should be "Joules", so 128 chars should be plenty.
502 char unit[128];
503 if (!ReadValueFromPowerFile("events/energy-", aName, ".unit", "%127s",
15
Taking true branch
504 unit)) {
505 Abort("failed to read from .unit file");
506 }
507 if (strcmp(unit, "Joules") != 0) {
16
Assuming the condition is false
17
Taking false branch
508 Abort("unexpected unit '%s' in .unit file", unit);
509 }
510
511 struct perf_event_attr attr;
512 memset(&attr, 0, sizeof(attr));
513 attr.type = aType;
514 attr.size = uint32_t(sizeof(attr));
515 attr.config = config;
18
Assigned value is uninitialized
516
517 // Measure all processes/threads. The specified CPU doesn't matter.
518 mFd = perf_event_open(&attr, /* aPid = */ -1, /* aCpu = */ 0,
519 /* aGroupFd = */ -1, /* aFlags = */ 0);
520 if (mFd < 0) {
521 Abort(
522 "perf_event_open() failed\n"
523 "- Did you run as root (e.g. with |sudo|) or set\n"
524 " /proc/sys/kernel/perf_event_paranoid to 0, as required?");
525 }
526
527 mPrevTicks = 0;
528 }
529
530 ~Domain() {
531 if (mIsSupported) {
532 close(mFd);
533 }
534 }
535
536 double EnergyEstimate() {
537 if (!mIsSupported) {
538 return kUnsupported_j;
539 }
540
541 uint64_t thisTicks;
542 if (read(mFd, &thisTicks, sizeof(uint64_t)) != sizeof(uint64_t)) {
543 Abort("read() failed");
544 }
545
546 uint64_t ticks = thisTicks - mPrevTicks;
547 mPrevTicks = thisTicks;
548 double joules = ticks * mJoulesPerTick;
549 return joules;
550 }
551};
552
553class RAPL {
554 Domain* mPkg;
555 Domain* mCores;
556 Domain* mGpu;
557 Domain* mRam;
558
559 public:
560 RAPL() {
561 uint32_t type;
562 if (!ReadValueFromPowerFile("type", "", "", "%u", &type)) {
5
Taking false branch
563 Abort("failed to read from type file");
564 }
565
566 mPkg = new Domain("pkg", type);
6
Calling constructor for 'Domain'
567 mCores = new Domain("cores", type);
568 mGpu = new Domain("gpu", type, Domain::Optional);
569 mRam = new Domain("ram", type, Domain::Optional);
570 if (!mPkg || !mCores || !mGpu || !mRam) {
571 Abort("new Domain() failed");
572 }
573 }
574
575 ~RAPL() {
576 delete mPkg;
577 delete mCores;
578 delete mGpu;
579 delete mRam;
580 }
581
582 void EnergyEstimates(double& aPkg_J, double& aCores_J, double& aGpu_J,
583 double& aRam_J) {
584 aPkg_J = mPkg->EnergyEstimate();
585 aCores_J = mCores->EnergyEstimate();
586 aGpu_J = mGpu->EnergyEstimate();
587 aRam_J = mRam->EnergyEstimate();
588 }
589};
590
591#else
592
593//---------------------------------------------------------------------------
594// Unsupported platforms
595//---------------------------------------------------------------------------
596
597# error Sorry, this platform is not supported
598
599#endif // platform
600
601//---------------------------------------------------------------------------
602// The main loop
603//---------------------------------------------------------------------------
604
605// The sample interval, measured in seconds.
606static double gSampleInterval_sec;
607
608// The platform-specific RAPL-reading machinery.
609static RAPL* gRapl;
610
611// All the sampled "total" values, in Watts.
612MOZ_GLIBCXX_CONSTINIT static std::vector<double> gTotals_W;
613
614// Power = Energy / Time, where power is measured in Watts, Energy is measured
615// in Joules, and Time is measured in seconds.
616static double JoulesToWatts(double aJoules) {
617 return aJoules / gSampleInterval_sec;
618}
619
620// "Normalize" here means convert kUnsupported_j to zero so it can be used in
621// additive expressions. All printed values are 5 or maybe 6 chars (though 6
622// chars would require a value > 100 W, which is unlikely). Values above 1000 W
623// are normalized to " n/a ", so 6 chars is the longest that may be printed.
624static void NormalizeAndPrintAsWatts(char* aBuf, double& aValue_J) {
625 if (aValue_J == kUnsupported_j || aValue_J >= 1000) {
626 aValue_J = 0;
627 sprintf(aBuf, "%s", " n/a ");
628 } else {
629 sprintf(aBuf, "%5.2f", JoulesToWatts(aValue_J));
630 }
631}
632
633static void SigAlrmHandler(int aSigNum, siginfo_t* aInfo, void* aContext) {
634 static int sampleNumber = 1;
635
636 double pkg_J, cores_J, gpu_J, ram_J;
637 gRapl->EnergyEstimates(pkg_J, cores_J, gpu_J, ram_J);
638
639 // We should have pkg and cores estimates, but might not have gpu and ram
640 // estimates.
641 assert(pkg_J != kUnsupported_j)(static_cast <bool> (pkg_J != kUnsupported_j) ? void (0
) : __assert_fail ("pkg_J != kUnsupported_j", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
642 assert(cores_J != kUnsupported_j)(static_cast <bool> (cores_J != kUnsupported_j) ? void (
0) : __assert_fail ("cores_J != kUnsupported_j", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
643
644 // This needs to be big enough to print watt values to two decimal places. 16
645 // should be plenty.
646 static const size_t kNumStrLen = 16;
647
648 static char pkgStr[kNumStrLen], coresStr[kNumStrLen], gpuStr[kNumStrLen],
649 ramStr[kNumStrLen];
650 NormalizeAndPrintAsWatts(pkgStr, pkg_J);
651 NormalizeAndPrintAsWatts(coresStr, cores_J);
652 NormalizeAndPrintAsWatts(gpuStr, gpu_J);
653 NormalizeAndPrintAsWatts(ramStr, ram_J);
654
655 // Core and GPU power are a subset of the package power.
656 assert(pkg_J >= cores_J + gpu_J)(static_cast <bool> (pkg_J >= cores_J + gpu_J) ? void
(0) : __assert_fail ("pkg_J >= cores_J + gpu_J", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
657
658 // Compute "other" (i.e. rest of the package) and "total" only after the
659 // other values have been normalized.
660
661 char otherStr[kNumStrLen];
662 double other_J = pkg_J - cores_J - gpu_J;
663 NormalizeAndPrintAsWatts(otherStr, other_J);
664
665 char totalStr[kNumStrLen];
666 double total_J = pkg_J + ram_J;
667 NormalizeAndPrintAsWatts(totalStr, total_J);
668
669 gTotals_W.push_back(JoulesToWatts(total_J));
670
671 // Print and flush so that the output appears immediately even if being
672 // redirected through |tee| or anything like that.
673 PrintAndFlush("#%02d %s W = %s (%s + %s + %s) + %s W\n", sampleNumber++,
674 totalStr, pkgStr, coresStr, gpuStr, otherStr, ramStr);
675}
676
677static void Finish() {
678 size_t n = gTotals_W.size();
679
680 // This time calculation assumes that the timers are perfectly accurate which
681 // is not true but the inaccuracy should be small in practice.
682 double time = n * gSampleInterval_sec;
683
684 printf("\n");
685 printf("%d sample%s taken over a period of %.3f second%s\n", int(n),
686 n == 1 ? "" : "s", n * gSampleInterval_sec, time == 1.0 ? "" : "s");
687
688 if (n == 0 || n == 1) {
689 exit(0);
690 }
691
692 // Compute the mean.
693 double sum = std::accumulate(gTotals_W.begin(), gTotals_W.end(), 0.0);
694 double mean = sum / n;
695
696 // Compute the *population* standard deviation:
697 //
698 // popStdDev = sqrt(Sigma(x - m)^2 / n)
699 //
700 // where |x| is the sum variable, |m| is the mean, and |n| is the
701 // population size.
702 //
703 // This is different from the *sample* standard deviation, which divides by
704 // |n - 1|, and would be appropriate if we were using a random sample of a
705 // larger population.
706 double sumOfSquaredDeviations = 0;
707 for (double& iter : gTotals_W) {
708 double deviation = (iter - mean);
709 sumOfSquaredDeviations += deviation * deviation;
710 }
711 double popStdDev = sqrt(sumOfSquaredDeviations / n);
712
713 // Sort so that percentiles can be determined. We use the "Nearest Rank"
714 // method of determining percentiles, which is simplest to compute and which
715 // chooses values from those that appear in the input set.
716 std::sort(gTotals_W.begin(), gTotals_W.end());
717
718 printf("\n");
719 printf("Distribution of 'total' values:\n");
720 printf(" mean = %5.2f W\n", mean);
721 printf(" std dev = %5.2f W\n", popStdDev);
722 printf(" 0th percentile = %5.2f W (min)\n", gTotals_W[0]);
723 printf(" 5th percentile = %5.2f W\n", gTotals_W[ceil(0.05 * n) - 1]);
724 printf(" 25th percentile = %5.2f W\n", gTotals_W[ceil(0.25 * n) - 1]);
725 printf(" 50th percentile = %5.2f W\n", gTotals_W[ceil(0.50 * n) - 1]);
726 printf(" 75th percentile = %5.2f W\n", gTotals_W[ceil(0.75 * n) - 1]);
727 printf(" 95th percentile = %5.2f W\n", gTotals_W[ceil(0.95 * n) - 1]);
728 printf("100th percentile = %5.2f W (max)\n", gTotals_W[n - 1]);
729
730 exit(0);
731}
732
733static void SigIntHandler(int aSigNum, siginfo_t* aInfo, void* aContext) {
734 Finish();
735}
736
737static void PrintUsage() {
738 printf(
739 "usage: rapl [options]\n"
740 "\n"
741 "Options:\n"
742 "\n"
743 " -h --help show this message\n"
744 " -i --sample-interval <N> sample every N ms [default=1000]\n"
745 " -n --sample-count <N> get N samples (0 means unlimited) "
746 "[default=0]\n"
747 "\n"
748#if defined(__APPLE__)
749 "On Mac this program can be run by any user.\n"
750#elif defined(__linux__1)
751 "On Linux this program can only be run by the super-user unless the "
752 "contents\n"
753 "of /proc/sys/kernel/perf_event_paranoid is set to 0 or lower.\n"
754#else
755# error Sorry, this platform is not supported
756#endif
757 "\n");
758}
759
760int main(int argc, char** argv) {
761 // Process command line options.
762
763 gArgv0 = argv[0];
764
765 // Default values.
766 int sampleInterval_msec = 1000;
767 int sampleCount = 0;
768
769 struct option longOptions[] = {
770 {"help", no_argument0, nullptr, 'h'},
771 {"sample-interval", required_argument1, nullptr, 'i'},
772 {"sample-count", required_argument1, nullptr, 'n'},
773 {nullptr, 0, nullptr, 0}};
774 const char* shortOptions = "hi:n:";
775
776 int c;
777 char* endPtr;
778 while ((c = getopt_long(argc, argv, shortOptions, longOptions, nullptr)) !=
1
Assuming the condition is false
2
Loop condition is false. Execution continues on line 822
779 -1) {
780 switch (c) {
781 case 'h':
782 PrintUsage();
783 exit(0);
784
785 case 'i':
786 sampleInterval_msec = strtol(optarg, &endPtr, /* base = */ 10);
787 if (*endPtr) {
788 CmdLineAbort("sample interval is not an integer");
789 }
790 if (sampleInterval_msec < 1 || sampleInterval_msec > 3600000) {
791 CmdLineAbort("sample interval must be in the range 1..3600000 ms");
792 }
793 break;
794
795 case 'n':
796 sampleCount = strtol(optarg, &endPtr, /* base = */ 10);
797 if (*endPtr) {
798 CmdLineAbort("sample count is not an integer");
799 }
800 if (sampleCount < 0 || sampleCount > 1000000) {
801 CmdLineAbort("sample count must be in the range 0..1000000");
802 }
803 break;
804
805 default:
806 CmdLineAbort(nullptr);
807 }
808 }
809
810 // The RAPL MSRs update every ~1 ms, but the measurement period isn't exactly
811 // 1 ms, which means the sample periods are not exact. "Power Measurement
812 // Techniques on Standard Compute Nodes: A Quantitative Comparison" by
813 // Hackenberg et al. suggests the following.
814 //
815 // "RAPL provides energy (and not power) consumption data without
816 // timestamps associated to each counter update. This makes sampling rates
817 // above 20 Samples/s unfeasible if the systematic error should be below
818 // 5%... Constantly polling the RAPL registers will both occupy a processor
819 // core and distort the measurement itself."
820 //
821 // So warn about this case.
822 if (sampleInterval_msec
2.1
'sampleInterval_msec' is >= 50
< 50) {
3
Taking false branch
823 fprintf(stderrstderr,
824 "\nWARNING: sample intervals < 50 ms are likely to produce "
825 "inaccurate estimates\n\n");
826 }
827 gSampleInterval_sec = double(sampleInterval_msec) / 1000;
828
829 // Initialize the platform-specific RAPL reading machinery.
830 gRapl = new RAPL();
4
Calling default constructor for 'RAPL'
831 if (!gRapl) {
832 Abort("new RAPL() failed");
833 }
834
835 // Install the signal handlers.
836
837 struct sigaction sa;
838 memset(&sa, 0, sizeof(sa));
839 sa.sa_flags = SA_RESTART0x10000000 | SA_SIGINFO4;
840 // The extra parens around (0) suppress a -Wunreachable-code warning on OS X
841 // where sigemptyset() is a macro that can never fail and always returns 0.
842 if (sigemptyset(&sa.sa_mask) < (0)) {
843 Abort("sigemptyset() failed");
844 }
845 sa.sa_sigaction__sigaction_handler.sa_sigaction = SigAlrmHandler;
846 if (sigaction(SIGALRM14, &sa, nullptr) < 0) {
847 Abort("sigaction(SIGALRM) failed");
848 }
849 sa.sa_sigaction__sigaction_handler.sa_sigaction = SigIntHandler;
850 if (sigaction(SIGINT2, &sa, nullptr) < 0) {
851 Abort("sigaction(SIGINT) failed");
852 }
853
854 // Set up the timer.
855 struct itimerval timer;
856 timer.it_interval.tv_sec = sampleInterval_msec / 1000;
857 timer.it_interval.tv_usec = (sampleInterval_msec % 1000) * 1000;
858 timer.it_value = timer.it_interval;
859 if (setitimer(ITIMER_REALITIMER_REAL, &timer, nullptr) < 0) {
860 Abort("setitimer() failed");
861 }
862
863 // Print header.
864 PrintAndFlush(" total W = _pkg_ (cores + _gpu_ + other) + _ram_ W\n");
865
866 // Take samples.
867 if (sampleCount == 0) {
868 while (true) {
869 pause();
870 }
871 } else {
872 for (int i = 0; i < sampleCount; i++) {
873 pause();
874 }
875 }
876
877 Finish();
878
879 return 0;
880}