跳转至

base/system/cpu/private/linux_impl/cpu_bonus.cpp

CPU Bonus Information Query Implementation (Linux) More...

Functions

Name
cf::expected< void, cf::CPUBonusInfoViewError > query_cpu_bonus_info(cf::CPUBonusInfoHost & bonus)

Detailed Description

CPU Bonus Information Query Implementation (Linux)

Author: Charliechen114514 (chengh1922@mails.jlu.edu.cn)

Version: 0.1

Date: 2026-02-22

Copyright: Copyright © 2026

Functions Documentation

function query_cpu_bonus_info

cf::expected< void, cf::CPUBonusInfoViewError > query_cpu_bonus_info(
    cf::CPUBonusInfoHost & bonus
)

Source code

#include "cpu_bonus.h"
#include "base/linux/proc_parser.h"
#include "cpu_features.h"

#include <cstdio>
#include <cstring>
#include <dirent.h>
#include <optional>
#include <string_view>
#include <unistd.h>

namespace {

// Buffer size for sysfs paths
// Need space for: /sys/class/thermal/ (18) + d_name (255) + /type or /temp (5) + null
constexpr size_t PATH_BUFFER_SIZE = 320;

uint32_t readCacheSize(int cpu, int level) noexcept {
    char path[PATH_BUFFER_SIZE];
    snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cache/index%d/size", cpu, level);

    FILE* file = fopen(path, "r");
    if (!file) {
        return 0;
    }

    char sizeStr[32];
    if (fgets(sizeStr, sizeof(sizeStr), file) == nullptr) {
        fclose(file);
        return 0;
    }
    fclose(file);

    // Parse size (e.g., "32K" or "1M") using modern utilities
    const std::string_view size_sv(sizeStr);
    if (auto size = cf::parse_cache_size(size_sv)) {
        return *size;
    }
    return 0;
}

void fillCacheSizes(cf::CPUBonusInfoHost& host) {
    host.cache_size.resize(3); // L1, L2, L3

    // Read from CPU0 (typical for all cores)
    for (int level = 0; level < 3; ++level) {
        uint32_t size = readCacheSize(0, level);
        host.cache_size[level] = size;
    }
}

void detectBigLittleCores(cf::CPUBonusInfoHost& host) {
    // Method 1: Check CPU capacities from /sys/devices/system/cpu/cpu*/cpu_capacity
    bool hasCapacityInfo = false;

    int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
    if (cpuCount < 0) {
        cpuCount = 1;
    }

    for (int cpu = 0; cpu < cpuCount; ++cpu) {
        char capacityPath[PATH_BUFFER_SIZE];
        snprintf(capacityPath, sizeof(capacityPath), "/sys/devices/system/cpu/cpu%d/cpu_capacity",
                 cpu);

        if (auto capacity = cf::read_uint32_file(capacityPath)) {
            hasCapacityInfo = true;
            // Determine threshold for big vs little (typically around 512-600)
            // Big cores have higher capacity values
            if (*capacity > 550) {
                host.big_core_count++;
            } else {
                host.little_core_count++;
            }
        } else {
            break; // No more CPUs with capacity info
        }
    }

    if (hasCapacityInfo) {
        host.has_big_little = (host.big_core_count > 0 && host.little_core_count > 0);
        return;
    }

    // Method 2: Check max frequencies of different CPUs
    uint32_t maxFreq = 0;
    uint32_t minFreq = UINT32_MAX;

    for (int cpu = 0; cpu < cpuCount; ++cpu) {
        char freqPath[PATH_BUFFER_SIZE];
        snprintf(freqPath, sizeof(freqPath),
                 "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);

        if (auto freq = cf::read_uint32_file(freqPath)) {
            if (*freq > maxFreq) {
                maxFreq = *freq;
            }
            if (*freq < minFreq) {
                minFreq = *freq;
            }
        }
    }

    // If there's a significant frequency difference, we have big.LITTLE
    if (maxFreq > 0 && minFreq < UINT32_MAX && (maxFreq - minFreq) > 200000) {
        // Count cores at each frequency level
        for (int cpu = 0; cpu < cpuCount; ++cpu) {
            char freqPath[PATH_BUFFER_SIZE];
            snprintf(freqPath, sizeof(freqPath),
                     "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu);

            if (auto freq = cf::read_uint32_file(freqPath)) {
                if (*freq >= maxFreq - 100000) {
                    host.big_core_count++;
                } else {
                    host.little_core_count++;
                }
            }
        }

        host.has_big_little = (host.big_core_count > 0 && host.little_core_count > 0);
    }
}

std::optional<uint16_t> readCpuTemperature() noexcept {
    // Try to find CPU thermal zone
    const char* thermalBasePath = "/sys/class/thermal";

    DIR* dir = opendir(thermalBasePath);
    if (!dir) {
        return std::nullopt;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (strncmp(entry->d_name, "thermal_zone", 12) == 0) {
            char typePath[PATH_BUFFER_SIZE];
            snprintf(typePath, sizeof(typePath), "%s/%s/type", thermalBasePath, entry->d_name);

            FILE* typeFile = fopen(typePath, "r");
            if (typeFile) {
                char type[64];
                if (fgets(type, sizeof(type), typeFile)) {
                    // Remove trailing newline
                    size_t len = strlen(type);
                    if (len > 0 && type[len - 1] == '\n') {
                        type[len - 1] = '\0';
                    }

                    // Look for CPU-related thermal zones
                    const std::string_view type_sv(type);
                    constexpr std::string_view cpu_marker = "cpu";
                    constexpr std::string_view CPU_marker = "CPU";
                    constexpr std::string_view x86_marker = "x86";

                    if (type_sv.find(cpu_marker) != std::string_view::npos ||
                        type_sv.find(CPU_marker) != std::string_view::npos ||
                        type_sv.find(x86_marker) != std::string_view::npos) {

                        char tempPath[PATH_BUFFER_SIZE];
                        snprintf(tempPath, sizeof(tempPath), "%s/%s/temp", thermalBasePath,
                                 entry->d_name);

                        FILE* tempFile = fopen(tempPath, "r");
                        if (tempFile) {
                            int tempMillidegree = 0;
                            if (fscanf(tempFile, "%d", &tempMillidegree) == 1) {
                                fclose(tempFile);
                                closedir(dir);
                                return static_cast<uint16_t>(tempMillidegree /
                                                             1000); // Convert to Celsius
                            }
                            fclose(tempFile);
                        }
                    }
                }
                fclose(typeFile);
            }
        }
    }

    closedir(dir);
    return std::nullopt;
}

} // namespace

cf::expected<void, cf::CPUBonusInfoViewError> query_cpu_bonus_info(cf::CPUBonusInfoHost& bonus) {
    // Get CPU features
    query_cpu_features(bonus.features);

    // Get cache sizes
    fillCacheSizes(bonus);

    // Detect big.LITTLE architecture
    detectBigLittleCores(bonus);

    // Try to read CPU temperature
    bonus.temperature = readCpuTemperature();

    return {};
}

Updated on 2026-03-09 at 10:14:01 +0000