Skip to content

Dedup Linux CentralProcessor topology, frequency, and load-average logic - #3463

Merged
dbwiddis merged 1 commit into
oshi:masterfrom
dbwiddis:linux-cpu-dedup
Jul 15, 2026
Merged

Dedup Linux CentralProcessor topology, frequency, and load-average logic#3463
dbwiddis merged 1 commit into
oshi:masterfrom
dbwiddis:linux-cpu-dedup

Conversation

@dbwiddis

@dbwiddis dbwiddis commented Jul 15, 2026

Copy link
Copy Markdown
Member

Completes the CentralProcessor cross-OS consistency/dedup arc (after Windows #3459, FreeBSD/DragonFly #3460, OpenBSD #3461, and macOS #3462), now for Linux.

What

The Linux central processor's JNA/FFM/native-free backends duplicated ~130 lines of near-identical udev topology and frequency logic, plus separate getSystemLoadAverage overrides. The only part that genuinely differs between JNA and FFM is the udev enumeration itself; everything downstream (syspath parsing, cpufreq file reads, topology building, MODALIAS) is plain Java. So the entire divergence collapses to two hooks:

  • enumerateCpuSyspathsViaUdev() — returns the CPU syspaths via udev, or null when udev is unavailable so the base falls back to a sysfs directory scan. JNA (UdevContext), FFM (UdevFunctions), native-free (null).
  • getloadavgNative() — native getloadavg; the native-free backend returns -1 so the base falls back to /proc/loadavg.

The base LinuxCentralProcessor now implements topology, current-frequency, max-frequency, and load-average once. getSystemLoadAverage is all-or-nothing (a partial native result blanks the whole array — a partial read is more likely a misread than a real load average), consistent with the other platforms, with a /proc/loadavg fallback.

Twins shrink: JNA 187→70, FFM 200→95, NF 51→48 (net −114 lines).

Behavior notes (both preserve observable output)

  1. Topology MODALIAS is now read from the cpu uevent file — the source the sysfs path already used and that udev itself derives the property from — rather than the udev device property.
  2. The no-udev frequency path now enumerates CPUs with the same sysfs scan used for topology.

Tests

Added stub-based getSystemLoadAverage coverage (full result, all-or-nothing partial, bounds), gated @EnabledOnOs(LINUX) because constructing a LinuxCentralProcessor initializes the Linux-only SysPath; plus cpuSyspathsFromSysfs enumeration tests that run everywhere. The existing topology tests pass unchanged through the new shared buildTopology.

Validated with a clean full build on macOS; the Linux runtime paths (udev/freq/topology) are exercised by the Linux CI matrix (JNA + FFM + native-free). No CHANGELOG — no user-facing behavior change.

Summary by CodeRabbit

  • Improvements
    • Improved Linux CPU topology discovery, with better handling when udev is unavailable.
    • Centralized CPU frequency reporting with improved fallbacks for current/max values.
    • Enhanced system load-average retrieval using native support when available, otherwise /proc, including stricter handling of partial/invalid results.
    • Native-free Linux now relies solely on sysfs and /proc for these metrics.
  • Tests
    • Added OS-conditional and stubbed test coverage for load-average edge cases and CPU syspath scanning.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c8c997e5-f8a2-4ebc-a546-a422ee3fba37

📥 Commits

Reviewing files that changed from the base of the PR and between 651942b and 2eeb289.

📒 Files selected for processing (5)
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-common/src/test/java/oshi/hardware/common/platform/linux/LinuxCentralProcessorTest.java
  • oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • oshi-common/src/test/java/oshi/hardware/common/platform/linux/LinuxCentralProcessorTest.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
📜 Recent review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Test JDK 21, windows-latest
  • GitHub Check: Test JDK 26, windows-latest
  • GitHub Check: Test JDK 25, macos-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 26, macos-latest
  • GitHub Check: Test JDK 21, macos-latest
  • GitHub Check: Test JDK 25, ubuntu-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 25, windows-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 25, FreeBSD (+ JNA==FFM)
  • GitHub Check: Test JDK 25, OpenBSD (+ JNA==FFM)
  • GitHub Check: Lint (Spotless, Checkstyle, ForbiddenAPIs, Javadoc)
  • GitHub Check: Test JDK 25, OpenIndiana (+ JNA==FFM)
  • GitHub Check: Analyze (java)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, only interfaces and classes in the root packages `oshi`, `oshi.hardware`, and `oshi.software.os` are considered the public API. Code in subpackages that represent specific implementations/platforms (e.g., `oshi.software.os.linux`, `oshi.hardware.platform.linux`, and similar subpackages) is implementation detail. When reviewing, do not treat changes to classes in these platform-specific subpackages as public API breaks (for example: making `LinuxOperatingSystem` abstract, narrowing constructor visibility, or adding/removing abstract methods should not be flagged as breaking public API).

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
📚 Learning: 2026-07-06T19:25:59.225Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3432
File: oshi-common/src/main/java/oshi/util/ExceptionUtil.java:26-56
Timestamp: 2026-07-06T19:25:59.225Z
Learning: In this repo’s SLF4J 1.x-compatible logging code, do not flag cases where a logger call uses SLF4J 1.6+ implicit-cause handling—e.g., `log.error("...{}", value, t)` (or similar with a single `{}` placeholder) where `t` is passed as the final/trailing argument. SLF4J will attach `t` as the log event’s cause with the full stack trace; this is equivalent to SLF4J 2.x’s fluent `atLevel(...).setCause(t).log(...)` and is intentionally used to preserve SLF4J 1.x runtime compatibility.

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
🔇 Additional comments (4)
oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java (4)

56-64: The earlier udev fallback comment remains unresolved.

Failure results from subsystem matching/scanning and invalid entry.getName() values can still produce an empty or invalid list instead of null, preventing the shared sysfs fallback.


11-13: LGTM!


28-38: LGTM!


80-81: LGTM!


📝 Walkthrough

Walkthrough

LinuxCentralProcessor centralizes Linux CPU topology, cpufreq, and load-average handling through CPU syspaths and native hooks. Native-free, FFM, and JNA implementations provide platform-specific hooks, with tests covering native-loadavg and sysfs fallbacks.

Changes

Linux CPU refactor

Layer / File(s) Summary
Shared topology, frequency, and load-average logic
oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
CPU topology and frequency queries use shared syspath enumeration with sysfs fallback; load averages use native retrieval with /proc/loadavg fallback and partial-result handling.
Platform-specific enumeration and native hooks
oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java, oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java, oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
Native-free, FFM, and JNA implementations provide udev and native load-average hooks while removing duplicated topology and frequency logic.
Fallback and native-loadavg tests
oshi-common/src/test/java/oshi/hardware/common/platform/linux/LinuxCentralProcessorTest.java
Tests cover native load-average results, partial results, invalid arguments, and sysfs CPU syspath discovery.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LinuxCentralProcessor
  participant NativeBackend
  participant ProcLoadavg
  LinuxCentralProcessor->>NativeBackend: getloadavgNative(loadavg, nelem)
  NativeBackend-->>LinuxCentralProcessor: samples or failure
  LinuxCentralProcessor->>ProcLoadavg: parse /proc/loadavg on native failure
  ProcLoadavg-->>LinuxCentralProcessor: load averages
Loading

Possibly related PRs

  • oshi/oshi#3120: Refactors overlapping Linux udev topology and cpufreq handling.
  • oshi/oshi#3188: Adds related sysfs topology parsing assertions.
  • oshi/oshi#3285: Overlaps with FFM Linux processor udev and native-call changes.

Poem

A rabbit hops through CPU lanes,
While syspaths map the core domains.
Native loads leap, then gently fall
To /proc when native calls stall.
FFM and JNA share the trail—
Tests guard each carrot without fail.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: deduplicating Linux CentralProcessor topology, frequency, and load-average logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java (1)

507-527: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve -1 for processors whose frequency cannot be read.

When one CPU succeeds but another lacks both cpufreq files, the method returns successfully with 0 for the unavailable CPU instead of the API's unknown-value sentinel.

Proposed fix
     protected boolean queryCurrentFreqFromUdev(long[] freqs) {
+        Arrays.fill(freqs, -1L);
         long max = 0L;
         for (String syspath : cpuSyspaths()) {
             int cpuIdx = ParseUtil.getFirstIntValue(syspath);
             if (cpuIdx >= 0 && cpuIdx < freqs.length) {
-                freqs[cpuIdx] = FileUtil.getLongFromFile(syspath + "/cpufreq/scaling_cur_freq");
-                if (freqs[cpuIdx] == 0) {
-                    freqs[cpuIdx] = FileUtil.getLongFromFile(syspath + "/cpufreq/cpuinfo_cur_freq");
+                long freq = FileUtil.getLongFromFile(syspath + "/cpufreq/scaling_cur_freq");
+                if (freq == 0) {
+                    freq = FileUtil.getLongFromFile(syspath + "/cpufreq/cpuinfo_cur_freq");
                 }
-                if (max < freqs[cpuIdx]) {
-                    max = freqs[cpuIdx];
+                if (freq > 0) {
+                    freqs[cpuIdx] = freq;
+                    max = Math.max(max, freq);
                 }
             }
         }
         if (max > 0L) {
             for (int i = 0; i < freqs.length; i++) {
-                freqs[i] *= 1000L;
+                if (freqs[i] > 0L) {
+                    freqs[i] *= 1000L;
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java`
around lines 507 - 527, Update queryCurrentFreqFromUdev so processors whose
scaling_cur_freq and cpuinfo_cur_freq values are both unavailable retain the
API’s -1 sentinel instead of remaining 0. Initialize or assign each unreadable
freqs entry to -1 while preserving successful readings, max-frequency detection,
and the existing scaling and return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java`:
- Around line 239-243: Update cpuSyspathsFromSysfs to enumerate only immediate
children of cpuPath instead of recursively traversing the full sysfs subtree.
Use a non-recursive directory listing and retain only entries whose names match
the direct cpuN pattern, excluding nested topology, cache, and cpufreq paths.

In
`@oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java`:
- Around line 46-54: In LinuxCentralProcessorJNA.java lines 46-54, guard
enumerateNew(), addMatchSubsystem(), scanDevices(), and UdevListEntry.getName()
so any failure returns null and triggers the shared sysfs fallback rather than
throwing or returning an empty result. Apply the same failure-to-null handling
in LinuxCentralProcessorFFM.java lines 55-59 for the corresponding udev
enumeration, subsystem/scan, and entry-name operations.

---

Outside diff comments:
In
`@oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java`:
- Around line 507-527: Update queryCurrentFreqFromUdev so processors whose
scaling_cur_freq and cpuinfo_cur_freq values are both unavailable retain the
API’s -1 sentinel instead of remaining 0. Initialize or assign each unreadable
freqs entry to -1 while preserving successful readings, max-frequency detection,
and the existing scaling and return behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b704ef71-3e2e-4e0b-ba69-8be00ed0b494

📥 Commits

Reviewing files that changed from the base of the PR and between 9ff2f54 and 651942b.

📒 Files selected for processing (5)
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-common/src/test/java/oshi/hardware/common/platform/linux/LinuxCentralProcessorTest.java
  • oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
📜 Review details
⏰ Context from checks skipped due to timeout. (14)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Test JDK 26, macos-latest
  • GitHub Check: Test JDK 21, windows-latest
  • GitHub Check: Test JDK 26, windows-latest
  • GitHub Check: Test JDK 25, macos-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 25, windows-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 25, ubuntu-latest (+ JNA==FFM)
  • GitHub Check: Test JDK 25, FreeBSD (+ JNA==FFM)
  • GitHub Check: Analyze (java)
  • GitHub Check: Test JDK 21, macos-latest
  • GitHub Check: Test JDK 26, ubuntu-latest
  • GitHub Check: Test JDK 25, OpenIndiana (+ JNA==FFM)
  • GitHub Check: Test JDK 25, OpenBSD (+ JNA==FFM)
  • GitHub Check: Lint (Spotless, Checkstyle, ForbiddenAPIs, Javadoc)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2026-04-12T19:22:09.716Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3149
File: oshi-common/src/main/java/oshi/hardware/common/platform/mac/MacCentralProcessor.java:159-163
Timestamp: 2026-04-12T19:22:09.716Z
Learning: When reviewing OSHI hardware platform code that uses or implements `CentralProcessor`/`PhysicalProcessor.getEfficiency()`, remember the API’s counterintuitive semantics: a *higher* efficiency-class value indicates *greater performance* (lower efficiency). P-cores/“performance cores” should therefore have higher values (e.g., 1) than E-cores/“efficiency cores” (e.g., 0), as documented in the `CentralProcessor` Javadoc. Do not flag code as incorrect solely because ARM_P_CORES or Intel P-cores are assigned higher efficiency values.

Applied to files:

  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
📚 Learning: 2026-04-12T19:34:56.879Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3149
File: oshi-common/src/main/java/oshi/hardware/common/platform/mac/MacCentralProcessor.java:159-163
Timestamp: 2026-04-12T19:34:56.879Z
Learning: In OSHI’s Java platform implementations, `PhysicalProcessor.getEfficiency()` / `EfficiencyClass` follow the same convention as Windows `PROCESSOR_RELATIONSHIP.EfficiencyClass`: a *higher* numeric efficiency class value represents *greater performance* and *less efficiency* (typically P-cores higher than E-cores). When reviewing, do not treat cases where P-cores are assigned higher efficiency/class values as a bug—this is intentional cross-platform consistency.

Applied to files:

  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
📚 Learning: 2026-07-06T19:25:59.225Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3432
File: oshi-common/src/main/java/oshi/util/ExceptionUtil.java:26-56
Timestamp: 2026-07-06T19:25:59.225Z
Learning: In this repo’s SLF4J 1.x-compatible logging code, do not flag cases where a logger call uses SLF4J 1.6+ implicit-cause handling—e.g., `log.error("...{}", value, t)` (or similar with a single `{}` placeholder) where `t` is passed as the final/trailing argument. SLF4J will attach `t` as the log event’s cause with the full stack trace; this is equivalent to SLF4J 2.x’s fluent `atLevel(...).setCause(t).log(...)` and is intentionally used to preserve SLF4J 1.x runtime compatibility.

Applied to files:

  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
  • oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java
  • oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java
📚 Learning: 2026-04-06T04:45:47.644Z
Learnt from: dbwiddis
Repo: oshi/oshi PR: 3121
File: oshi-core/src/main/java/oshi/software/os/linux/LinuxOperatingSystem.java:53-53
Timestamp: 2026-04-06T04:45:47.644Z
Learning: In the oshi/oshi project, only interfaces and classes in the root packages `oshi`, `oshi.hardware`, and `oshi.software.os` are considered the public API. Code in subpackages that represent specific implementations/platforms (e.g., `oshi.software.os.linux`, `oshi.hardware.platform.linux`, and similar subpackages) is implementation detail. When reviewing, do not treat changes to classes in these platform-specific subpackages as public API breaks (for example: making `LinuxOperatingSystem` abstract, narrowing constructor visibility, or adding/removing abstract methods should not be flagged as breaking public API).

Applied to files:

  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java
🔇 Additional comments (5)
oshi-common/src/main/java/oshi/hardware/common/platform/linux/LinuxCentralProcessor.java (1)

207-231: LGTM!

Also applies to: 251-272, 539-550, 580-593, 608-615

oshi-common/src/main/java/oshi/hardware/common/platform/linux/nativefree/LinuxCentralProcessorNF.java (1)

15-17: LGTM!

Also applies to: 37-45

oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java (1)

8-8: LGTM!

Also applies to: 30-31, 43-54, 60-71, 74-88

oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java (1)

25-26: LGTM!

Also applies to: 36-45, 55-59, 68-69

oshi-common/src/test/java/oshi/hardware/common/platform/linux/LinuxCentralProcessorTest.java (1)

26-27: LGTM!

Also applies to: 143-190, 502-529

Comment on lines +46 to +54
UdevEnumerate enumerate = udev.enumerateNew();
try {
enumerate.addMatchSubsystem("cpu");
enumerate.scanDevices();
for (UdevListEntry entry = enumerate.getListEntry(); entry != null; entry = entry.getNext()) {
syspaths.add(entry.getName());
}
} finally {
enumerate.unref();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Preserve the sysfs fallback when udev enumeration fails.
Guard udev.enumerateNew() / udev_enumerate_new(), the subsystem/scan calls, and entry.getName() in both the JNA and FFM paths; a failure here should return null so the shared sysfs fallback still runs instead of throwing or yielding an empty result.

📍 Affects 2 files
  • oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java#L46-L54 (this comment)
  • oshi-core-ffm/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorFFM.java#L55-L59
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@oshi-core/src/main/java/oshi/hardware/platform/linux/LinuxCentralProcessorJNA.java`
around lines 46 - 54, In LinuxCentralProcessorJNA.java lines 46-54, guard
enumerateNew(), addMatchSubsystem(), scanDevices(), and UdevListEntry.getName()
so any failure returns null and triggers the shared sysfs fallback rather than
throwing or returning an empty result. Apply the same failure-to-null handling
in LinuxCentralProcessorFFM.java lines 55-59 for the corresponding udev
enumeration, subsystem/scan, and entry-name operations.

Collapse the JNA/FFM/native-free divergence in the Linux central processor to
two hooks, moving all shared logic into LinuxCentralProcessor:

- enumerateCpuSyspathsViaUdev() returns the CPU syspaths via udev, or null when
  udev is unavailable so the base falls back to a sysfs directory scan. This is
  the only genuinely udev-specific operation (JNA UdevContext vs FFM
  UdevFunctions); everything downstream is now shared.
- getloadavgNative() performs the native getloadavg; the native-free backend
  returns -1 so the base falls back to /proc/loadavg.

The base now implements readTopologyWithUdev (via buildTopology over the shared
uevent-based reader), queryCurrentFreqFromUdev, queryMaxFreqFromUdev, and
getSystemLoadAverage once. getSystemLoadAverage is all-or-nothing: a partial
native result blanks the whole array (a partial result is more likely a misread
than a real load average), matching the other platforms, with a /proc/loadavg
fallback when the native call is unavailable.

Two internal behavior notes, both preserving observable output:
- Topology MODALIAS is now read from the cpu uevent file (the source the sysfs
  path already used and that udev itself derives the property from) rather than
  the udev device property.
- The no-udev frequency path now enumerates CPUs with the same sysfs scan used
  for topology.

Twins shrink substantially (JNA 187->70, FFM 200->95, NF 51->48). Add
stub-based getSystemLoadAverage coverage (full, all-or-nothing partial, bounds),
gated to Linux because constructing a LinuxCentralProcessor initializes the
Linux-only SysPath, plus cpuSyspathsFromSysfs enumeration tests.
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.38889% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.92%. Comparing base (9ff2f54) to head (2eeb289).

Files with missing lines Patch % Lines
...dware/platform/linux/LinuxCentralProcessorJNA.java 61.11% 6 Missing and 1 partial ⚠️
...e/common/platform/linux/LinuxCentralProcessor.java 84.21% 2 Missing and 4 partials ⚠️
...dware/platform/linux/LinuxCentralProcessorFFM.java 71.42% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3463      +/-   ##
============================================
+ Coverage     74.88%   74.92%   +0.03%     
  Complexity       76       76              
============================================
  Files           587      587              
  Lines         21715    21603     -112     
  Branches       3638     3611      -27     
============================================
- Hits          16262    16185      -77     
+ Misses         3446     3434      -12     
+ Partials       2007     1984      -23     
Flag Coverage Δ
freebsd 27.45% <18.05%> (+0.15%) ⬆️
linux 31.94% <76.38%> (-0.11%) ⬇️
macos 32.44% <18.05%> (+0.13%) ⬆️
openbsd 26.79% <18.05%> (+0.15%) ⬆️
solaris 29.14% <18.05%> (+0.17%) ⬆️
windows 40.73% <18.05%> (+0.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dbwiddis
dbwiddis merged commit bc6e8a7 into oshi:master Jul 15, 2026
19 checks passed
@dbwiddis
dbwiddis deleted the linux-cpu-dedup branch July 19, 2026 04:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant