base/system/memory/private/linux_impl/dimm_info.cpp¶
DIMM (Memory Module) Information Query Implementation (Linux) More...
Namespaces¶
| Name |
|---|
| cf |
| cf::linux_impl |
Detailed Description¶
DIMM (Memory Module) Information Query Implementation (Linux)
Author: Charliechen114514 (chengh1922@mails.jlu.edu.cn)
Version: 0.1
Date: 2026-02-27
Copyright: Copyright © 2026
Source code¶
#include "dimm_info.h"
#include <cstdio>
#include <cstring>
#include <memory>
namespace cf {
namespace linux_impl {
namespace {
MemoryType parseMemoryType(const char* typeStr) {
if (typeStr == nullptr) {
return MemoryType::UNKNOWN;
}
// Case-insensitive comparison
auto match = [](const char* str, const char* pattern) -> bool {
if (str == nullptr || pattern == nullptr)
return false;
size_t len = strlen(pattern);
if (strlen(str) < len)
return false;
return strncasecmp(str, pattern, len) == 0;
};
if (match(typeStr, "DDR3"))
return MemoryType::DDR3;
if (match(typeStr, "DDR4"))
return MemoryType::DDR4;
if (match(typeStr, "DDR5"))
return MemoryType::DDR5;
if (match(typeStr, "LPDDR3"))
return MemoryType::LPDDR3;
if (match(typeStr, "LPDDR4"))
return MemoryType::LPDDR4;
if (match(typeStr, "LPDDR4X"))
return MemoryType::LPDDR4X;
if (match(typeStr, "LPDDR5"))
return MemoryType::LPDDR5;
if (match(typeStr, "DDR2"))
return MemoryType::DDR2;
if (match(typeStr, "SDRAM"))
return MemoryType::SDRAM;
return MemoryType::UNKNOWN;
}
uint64_t parseMemorySize(const char* sizeStr) {
if (sizeStr == nullptr || strcmp(sizeStr, "No Module Installed") == 0) {
return 0;
}
unsigned long value = 0;
char unit[16] = {0};
if (sscanf(sizeStr, "%lu %15s", &value, unit) == 2) {
if (strcasecmp(unit, "MB") == 0 || strcasecmp(unit, "M") == 0) {
return value * 1024 * 1024;
} else if (strcasecmp(unit, "GB") == 0 || strcasecmp(unit, "G") == 0) {
return value * 1024 * 1024 * 1024;
} else if (strcasecmp(unit, "KB") == 0 || strcasecmp(unit, "K") == 0) {
return value * 1024;
}
// Default to MB if no unit specified
return value * 1024 * 1024;
}
return 0;
}
uint32_t parseFrequency(const char* freqStr) {
if (freqStr == nullptr || strcmp(freqStr, "Unknown") == 0) {
return 0;
}
unsigned long value = 0;
if (sscanf(freqStr, "%lu", &value) == 1) {
return static_cast<uint32_t>(value);
}
return 0;
}
std::string readFile(const char* path) {
FILE* fp = fopen(path, "r");
if (!fp) {
return "";
}
char buffer[256] = {0};
if (fgets(buffer, sizeof(buffer), fp)) {
// Remove trailing newline
size_t len = strlen(buffer);
while (len > 0 && (buffer[len - 1] == '\n' || buffer[len - 1] == '\r')) {
buffer[--len] = '\0';
}
fclose(fp);
return std::string(buffer);
}
fclose(fp);
return "";
}
bool parseDmidecode(const char* output, std::vector<DimmInfo>& dimms) {
if (output == nullptr) {
return false;
}
const char* p = output;
DimmInfo currentDimm{};
bool inDevice = false;
bool hasSize = false;
while (*p != '\0') {
// Skip leading whitespace
while (*p == ' ' || *p == '\t') {
p++;
}
// Check for "Memory Device" marker
if (strncmp(p, "Memory Device", 13) == 0) {
// Save previous device if valid
if (inDevice && hasSize && currentDimm.capacity_bytes > 0) {
dimms.push_back(currentDimm);
}
// Start new device
currentDimm = DimmInfo{};
inDevice = true;
hasSize = false;
// Skip to next line
while (*p != '\0' && *p != '\n')
p++;
if (*p == '\n')
p++;
continue;
}
if (inDevice) {
// Check for field indentation (typically fields start with 4-8 spaces)
// Check for end of device (empty line or next handle)
if (*p == '\n' || (*p == '\0')) {
if (*p == '\n')
p++;
// Save device if valid
if (hasSize && currentDimm.capacity_bytes > 0) {
dimms.push_back(currentDimm);
}
inDevice = false;
continue;
}
// Parse fields
auto extractField = [&p](const char* fieldName) -> char* {
char* result = nullptr;
size_t nameLen = strlen(fieldName);
// Check for field pattern: "FieldName: value"
if (strncmp(p, fieldName, nameLen) == 0 && p[nameLen] == ':') {
const char* valueStart = p + nameLen + 1;
while (*valueStart == ' ' || *valueStart == '\t') {
valueStart++;
}
// Find end of line
const char* valueEnd = valueStart;
while (*valueEnd != '\0' && *valueEnd != '\n') {
valueEnd++;
}
// Allocate and copy value
size_t valueLen = valueEnd - valueStart;
result = new char[valueLen + 1];
memcpy(result, valueStart, valueLen);
result[valueLen] = '\0';
p = valueEnd;
}
return result;
};
std::unique_ptr<char[]> field;
if ((field.reset(extractField("Size")), field)) {
currentDimm.capacity_bytes = parseMemorySize(field.get());
hasSize = true;
} else if ((field.reset(extractField("Type")), field)) {
currentDimm.type = parseMemoryType(field.get());
} else if ((field.reset(extractField("Speed")), field)) {
currentDimm.frequency_mhz = parseFrequency(field.get());
} else if ((field.reset(extractField("Manufacturer")), field)) {
currentDimm.manufacturer = field.get();
} else if ((field.reset(extractField("Serial Number")), field)) {
currentDimm.serial_number = field.get();
} else if ((field.reset(extractField("Part Number")), field)) {
currentDimm.part_number = field.get();
} else if ((field.reset(extractField("Locator")), field)) {
// Try to extract slot number from locator
// Common formats: "DIMM0", "ChannelA-DIMM0", "Slot 1", etc.
const char* slot = field.get();
// Simple heuristic: find last digit
const char* pDigit = slot + strlen(slot) - 1;
while (pDigit >= slot && (*pDigit < '0' || *pDigit > '9')) {
pDigit--;
}
if (pDigit >= slot) {
currentDimm.slot = static_cast<uint8_t>(*pDigit - '0');
}
}
}
// Move to next line
while (*p != '\0' && *p != '\n') {
p++;
}
if (*p == '\n') {
p++;
}
}
// Don't forget last device
if (inDevice && hasSize && currentDimm.capacity_bytes > 0) {
dimms.push_back(currentDimm);
}
return !dimms.empty();
}
bool queryDimmViaDmidecode(std::vector<DimmInfo>& dimms) {
FILE* pipe = popen("dmidecode -t memory 2>/dev/null", "r");
if (!pipe) {
return false;
}
// Read entire output
std::string output;
char buffer[4096];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
output += buffer;
}
int status = pclose(pipe);
if (status != 0) {
return false;
}
return parseDmidecode(output.c_str(), dimms);
}
bool queryDimmViaSysFs(std::vector<DimmInfo>& dimms) {
// /sys/class/dmi/id/ provides system-level info, not per-DIMM info
// We can get some basic board info but not detailed DIMM characteristics
// This is a minimal fallback that returns at least one entry with available info
std::string manufacturer = readFile("/sys/class/dmi/id/board_vendor");
std::string serial = readFile("/sys/class/dmi/id/board_serial");
std::string product = readFile("/sys/class/dmi/id/board_name");
// Check if we have any meaningful info
if (manufacturer.empty() && serial.empty() && product.empty()) {
return false;
}
// Create a minimal DIMM entry
DimmInfo dimm{};
dimm.manufacturer = manufacturer;
dimm.serial_number = serial;
dimm.part_number = product;
dimm.type = MemoryType::UNKNOWN; // Can't determine from /sys
dimm.capacity_bytes = 0; // Can't get individual DIMM size from /sys
dimm.slot = 0;
// Only add if we have some info (but capacity will be 0)
dimms.push_back(dimm);
return true;
}
} // anonymous namespace
void queryDimmInfo(std::vector<DimmInfo>& dimms) {
dimms.clear();
// Try dmidecode first (requires root)
if (!queryDimmViaDmidecode(dimms)) {
// Fall back to /sys/class/dmi/id/
queryDimmViaSysFs(dimms);
}
}
} // namespace linux_impl
} // namespace cf
Updated on 2026-03-09 at 10:14:01 +0000