| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title Os/Linux/Cpu.cpp | ||
| 3 | // \brief Linux implementation for Os::Cpu | ||
| 4 | // ====================================================================== | ||
| 5 | #include <unistd.h> | ||
| 6 | #include <Fw/Types/Assert.hpp> | ||
| 7 | #include <Fw/Types/StringUtils.hpp> | ||
| 8 | #include <Os/File.hpp> | ||
| 9 | #include <Os/Linux/Cpu.hpp> | ||
| 10 | #include <cstring> | ||
| 11 | namespace Os { | ||
| 12 | namespace Linux { | ||
| 13 | namespace Cpu { | ||
| 14 | |||
| 15 | // Proc FS /proc/stat file format example: | ||
| 16 | // | ||
| 17 | // cpu 270288 1598 88660 8470416 15185 9775 2990 867 0 0 | ||
| 18 | // cpu0 67462 108 22104 2118172 3779 2540 648 287 0 0 | ||
| 19 | // cpu1 74237 650 24059 2109680 2995 2447 667 93 0 0 | ||
| 20 | // cpu2 69388 630 22033 2115177 4428 2424 649 378 0 0 | ||
| 21 | // cpu3 59199 209 20462 2127387 3981 2363 1024 108 0 0 | ||
| 22 | // ... | ||
| 23 | |||
| 24 | // Needed format, and compliant with kernel < 2.5 | ||
| 25 | enum ProcCpuMeasures { CPU_NUMBER = 0, USER = 1, NICE = 2, SYSTEM = 3, IDLE = 4, MAX_CPU_TICK_TYPES = 8 }; | ||
| 26 | |||
| 27 | using ProcCpuData = FwSizeType[ProcCpuMeasures::MAX_CPU_TICK_TYPES]; | ||
| 28 | constexpr FwSizeType LINE_SIZE = 255; // log10(max(U64)) * 11 fields (kernel 2.6.33) = 220. Round to 256 - 1 (\0) | ||
| 29 | |||
| 30 | ✗ | CpuInterface::Status getCpuData(FwSizeType cpu_index, ProcCpuData data) { | |
| 31 | ✗ | Os::File file; | |
| 32 | // Open the procfs file | ||
| 33 | ✗ | constexpr char PROC_STAT_PATH[] = "/proc/stat"; | |
| 34 | ✗ | if (file.open(PROC_STAT_PATH, Os::File::Mode::OPEN_READ) != Os::File::Status::OP_OK) { | |
| 35 | ✗ | return CpuInterface::Status::ERROR; | |
| 36 | } | ||
| 37 | ✗ | char proc_stat_line[LINE_SIZE + 1]; | |
| 38 | // File starts with cpu line, then individual CPUs. | ||
| 39 | ✗ | const FwSizeType line_count = cpu_index + 2; | |
| 40 | ✗ | for (FwSizeType i = 0; i < line_count; i++) { | |
| 41 | ✗ | FwSizeType read_size = sizeof proc_stat_line - 1; | |
| 42 | Os::File::Status file_status = | ||
| 43 | ✗ | file.readline(reinterpret_cast<U8*>(proc_stat_line), read_size, Os::File::WaitType::NO_WAIT); | |
| 44 | ✗ | if (file_status != Os::File::Status::OP_OK) { | |
| 45 | ✗ | return CpuInterface::Status::ERROR; | |
| 46 | } | ||
| 47 | ✗ | proc_stat_line[read_size] = '\0'; // Null terminate, read_size is at most LINE_SIZE | |
| 48 | // Make sure we've not strayed passed the cpu section | ||
| 49 | ✗ | if (::strncmp(proc_stat_line, "cpu", 3) != 0) { | |
| 50 | ✗ | return CpuInterface::Status::ERROR; | |
| 51 | } | ||
| 52 | } | ||
| 53 | ✗ | char* token_start = proc_stat_line + 3; // Length of "cpu" | |
| 54 | ✗ | for (FwSizeType i = 0; i < ProcCpuMeasures::MAX_CPU_TICK_TYPES; i++) { | |
| 55 | ✗ | FwSizeType token = 0; | |
| 56 | ✗ | Fw::StringUtils::StringToNumberStatus status = Fw::StringUtils::string_to_number( | |
| 57 | token_start, | ||
| 58 | ✗ | static_cast<FwSizeType>(sizeof proc_stat_line) - static_cast<FwSizeType>(token_start - proc_stat_line), | |
| 59 | token, &token_start); | ||
| 60 | // Check conversion success | ||
| 61 | ✗ | if (status != Fw::StringUtils::StringToNumberStatus::SUCCESSFUL_CONVERSION) { | |
| 62 | ✗ | return CpuInterface::Status::ERROR; | |
| 63 | } | ||
| 64 | ✗ | data[i] = token; | |
| 65 | // Check cpu index is correct | ||
| 66 | ✗ | if (i == ProcCpuMeasures::CPU_NUMBER and data[i] != cpu_index) { | |
| 67 | ✗ | return CpuInterface::Status::ERROR; | |
| 68 | } | ||
| 69 | } | ||
| 70 | ✗ | return CpuInterface::Status::OP_OK; | |
| 71 | ✗ | } | |
| 72 | |||
| 73 | ✗ | CpuInterface::Status LinuxCpu::_getCount(FwSizeType& cpu_count) { | |
| 74 | ✗ | long cpus = sysconf(_SC_NPROCESSORS_ONLN); | |
| 75 | ✗ | if ((cpus > 0) && (static_cast<FwSizeType>(cpus) < std::numeric_limits<FwSizeType>::max())) { | |
| 76 | ✗ | cpu_count = static_cast<FwSizeType>(cpus); | |
| 77 | ✗ | return Status::OP_OK; | |
| 78 | } | ||
| 79 | ✗ | cpu_count = 0; | |
| 80 | ✗ | return Status::ERROR; | |
| 81 | } | ||
| 82 | |||
| 83 | ✗ | CpuInterface::Status LinuxCpu::_getTicks(Os::Cpu::Ticks& ticks, FwSizeType cpu_index) { | |
| 84 | ✗ | FwSizeType count = 0; | |
| 85 | ✗ | CpuInterface::Status status = this->_getCount(count); | |
| 86 | ✗ | if (status != CpuInterface::Status::OP_OK) { | |
| 87 | ✗ | return status; | |
| 88 | ✗ | } else if (cpu_index >= count) { | |
| 89 | ✗ | return CpuInterface::Status::ERROR; | |
| 90 | } | ||
| 91 | ✗ | ProcCpuData cpu_data = {0, 0, 0, 0, 0, 0, 0, 0}; | |
| 92 | ✗ | status = getCpuData(cpu_index, cpu_data); | |
| 93 | ✗ | ticks.used = cpu_data[ProcCpuMeasures::USER] + cpu_data[ProcCpuMeasures::NICE] + cpu_data[ProcCpuMeasures::SYSTEM]; | |
| 94 | ✗ | ticks.total = ticks.used + cpu_data[ProcCpuMeasures::IDLE]; | |
| 95 | |||
| 96 | ✗ | return status; | |
| 97 | } | ||
| 98 | |||
| 99 | ✗ | CpuHandle* LinuxCpu::getHandle() { | |
| 100 | ✗ | return &this->m_handle; | |
| 101 | } | ||
| 102 | |||
| 103 | } // namespace Cpu | ||
| 104 | } // namespace Linux | ||
| 105 | } // namespace Os | ||
| 106 |