diff --git a/src/applib/storage_property_descr.cpp b/src/applib/storage_property_descr.cpp index 2418bfa..30f9efa 100644 --- a/src/applib/storage_property_descr.cpp +++ b/src/applib/storage_property_descr.cpp @@ -115,6 +115,7 @@ bool storage_property_autoset_description(StorageProperty& p, StorageDeviceDetec found = auto_set(p, "ata_smart_attributes/revision", p.displayable_name.c_str()); if (!found) { auto_set_ata_attribute_description(p, device_type); + storage_property_ata_attribute_humanize_ssd_writes(p); found = true; // true, because auto_set_attr() may set "Unknown attribute", which is still "found". } break; diff --git a/src/applib/storage_property_descr_ata_attribute.cpp b/src/applib/storage_property_descr_ata_attribute.cpp index 09963ef..2be546d 100644 --- a/src/applib/storage_property_descr_ata_attribute.cpp +++ b/src/applib/storage_property_descr_ata_attribute.cpp @@ -22,6 +22,7 @@ License: GNU General Public License v3.0 only //#include "warning_colors.h" #include "storage_property_descr_helpers.h" #include "hz/string_num.h" +#include "hz/format_unit.h" // format_size namespace { @@ -1364,5 +1365,104 @@ void storage_property_ata_attribute_autoset_warning(StorageProperty& p) +void storage_property_ata_attribute_humanize_ssd_writes(StorageProperty& p) +{ + if (p.section != StoragePropertySection::AtaAttributes || !p.is_value_type()) { + return; + } + + const auto& attr = p.get_value(); + + // Skip if readable_value is already set (e.g., by parser or for GiB attributes) + if (!p.readable_value.empty()) { + return; + } + + // Standard sector size (512 bytes) + constexpr uint64_t bytes_per_sector = 512; + constexpr uint64_t mib_32 = 32ULL * 1024ULL * 1024ULL; + constexpr uint64_t gib = 1024ULL * 1024ULL * 1024ULL; + + // Match attribute by ID and reported name to handle vendor-specific attributes + const int32_t id = attr.id; + const std::string& name = p.reported_name; + std::optional bytes; + + // Write attributes - these need humanization most + // Attribute 199: Write_Sectors_Tot_Ct (Indilinx Barefoot SSDs) + // Total count of written sectors + if (id == 199 && name == "Write_Sectors_Tot_Ct") { + bytes = static_cast(attr.raw_value_int) * bytes_per_sector; + } + // Attribute 225: Host_Writes_32MiB (Intel SSDs) + else if (id == 225 && name == "Host_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 241: Host_Writes_32MiB (various SSDs) + // Raw value increased by 1 for every 32 MiB written + else if (id == 241 && name == "Host_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 243: Host_Writes_32MiB (SanDisk SSDs) + else if (id == 243 && name == "Host_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 245: Flash_Writes_32MiB (Innodisk SSDs) + else if (id == 245 && name == "Flash_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 245: TLC_Writes_32MiB (SiliconMotion SSDs) + else if (id == 245 && name == "TLC_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 246: SLC_Writes_32MiB (SiliconMotion SSDs) + else if (id == 246 && name == "SLC_Writes_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 246: Total_Host_Sector_Write (Crucial/Micron SSDs) + // Total number of sectors written by the host system + else if (id == 246 && name == "Total_Host_Sector_Write") { + bytes = static_cast(attr.raw_value_int) * bytes_per_sector; + } + // Attribute 249: NAND_Writes_1GiB (Intel SSDs) + // Note: The raw value is the count, not already in GiB + else if (id == 249 && name == "NAND_Writes_1GiB") { + bytes = static_cast(attr.raw_value_int) * gib; + } + // Attribute 249: Total_NAND_Prog_Ct_GiB (OCZ SSDs) + else if (id == 249 && name == "Total_NAND_Prog_Ct_GiB") { + bytes = static_cast(attr.raw_value_int) * gib; + } + + // Read attributes - also humanize for consistency + // Attribute 198: Read_Sectors_Tot_Ct (Indilinx Barefoot SSDs) + else if (id == 198 && name == "Read_Sectors_Tot_Ct") { + bytes = static_cast(attr.raw_value_int) * bytes_per_sector; + } + // Attribute 226: Host_Reads_32MiB (Intel SSDs) + else if (id == 226 && name == "Host_Reads_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 242: Host_Reads_32MiB (Intel SSDs) + else if (id == 242 && name == "Host_Reads_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 244: Flash_Reads_32MiB (Innodisk SSDs) + else if (id == 244 && name == "Flash_Reads_32MiB") { + bytes = static_cast(attr.raw_value_int) * mib_32; + } + // Attribute 251: Total_NAND_Read_Ct_GiB (OCZ SSDs) + else if (id == 251 && name == "Total_NAND_Read_Ct_GiB") { + bytes = static_cast(attr.raw_value_int) * gib; + } + + // Set readable_value if we determined the byte count + if (bytes.has_value() && bytes.value() > 0) { + // Use binary units (KiB, MiB, GiB, TiB) for consistency with existing attributes + p.readable_value = hz::format_size(bytes.value(), false); + } +} + + /// @} diff --git a/src/applib/storage_property_descr_ata_attribute.h b/src/applib/storage_property_descr_ata_attribute.h index f71f2c5..b4e3ff0 100644 --- a/src/applib/storage_property_descr_ata_attribute.h +++ b/src/applib/storage_property_descr_ata_attribute.h @@ -26,6 +26,11 @@ void auto_set_ata_attribute_description(StorageProperty& p, StorageDeviceDetecte void storage_property_ata_attribute_autoset_warning(StorageProperty& p); +/// Humanize SSD write statistics by converting raw values to readable byte counts. +/// Sets the readable_value field for applicable write-related attributes. +void storage_property_ata_attribute_humanize_ssd_writes(StorageProperty& p); + + #endif /// @}