@@ -10,6 +10,7 @@ import (
1010 htmltemplate "html/template"
1111 "log/slog"
1212 "math"
13+ "regexp"
1314 "sort"
1415 "strconv"
1516 "strings"
@@ -844,7 +845,7 @@ func elcTableInsights(outputs map[string]script.ScriptOutput, tableValues table.
844845 if err != nil {
845846 slog .Warn (err .Error ())
846847 } else {
847- // warn if ELC mode is not set to 'Latency Optimized' or 'Default ' consistently across all dies
848+ // warn if ELC mode is not set to 'Latency Optimized' or 'Optimized Power Mode ' consistently across all dies
848849 firstMode := tableValues .Fields [modeFieldIndex ].Values [0 ]
849850 for _ , mode := range tableValues .Fields [modeFieldIndex ].Values [1 :] {
850851 if mode != firstMode {
@@ -855,7 +856,7 @@ func elcTableInsights(outputs map[string]script.ScriptOutput, tableValues table.
855856 break
856857 }
857858 }
858- // suggest setting ELC mode to 'Latency Optimized' or 'Default ' based on the current setting
859+ // suggest setting ELC mode to 'Latency Optimized' or 'Optimized Power Mode ' based on the current setting
859860 for _ , mode := range tableValues .Fields [modeFieldIndex ].Values {
860861 if mode != "" && mode != extract .ELCModeLatencyOptimized {
861862 insights = append (insights , table.Insight {
@@ -1729,6 +1730,132 @@ func dimmDetails(dimm []string) (details string) {
17291730 return
17301731}
17311732
1733+ var configuredSpeedPattern = regexp .MustCompile (`(\d+(?:\.\d+)?\s*MT/s)\s*\[(\d+(?:\.\d+)?\s*MT/s)\]` )
1734+
1735+ func highlightConfiguredSpeedMismatchesHTML (value string ) string {
1736+ if value == "" {
1737+ return ""
1738+ }
1739+
1740+ matches := configuredSpeedPattern .FindAllStringSubmatchIndex (value , - 1 )
1741+ if len (matches ) == 0 {
1742+ return htmltemplate .HTMLEscapeString (value )
1743+ }
1744+
1745+ var sb strings.Builder
1746+ last := 0
1747+ for _ , match := range matches {
1748+ fullStart , fullEnd := match [0 ], match [1 ]
1749+ maxStart , maxEnd := match [2 ], match [3 ]
1750+ configuredStart , configuredEnd := match [4 ], match [5 ]
1751+
1752+ sb .WriteString (htmltemplate .HTMLEscapeString (value [last :fullStart ]))
1753+
1754+ maxSpeed := strings .TrimSpace (value [maxStart :maxEnd ])
1755+ configuredSpeed := strings .TrimSpace (value [configuredStart :configuredEnd ])
1756+ if maxSpeed != "" && configuredSpeed != "" && maxSpeed != configuredSpeed {
1757+ sb .WriteString (htmltemplate .HTMLEscapeString (value [fullStart :configuredStart ]))
1758+ sb .WriteString (`<span style="background-color:#fff3b0;font-weight:700">` )
1759+ sb .WriteString (htmltemplate .HTMLEscapeString (value [configuredStart :configuredEnd ]))
1760+ sb .WriteString (`</span>` )
1761+ sb .WriteString (htmltemplate .HTMLEscapeString (value [configuredEnd :fullEnd ]))
1762+ } else {
1763+ sb .WriteString (htmltemplate .HTMLEscapeString (value [fullStart :fullEnd ]))
1764+ }
1765+
1766+ last = fullEnd
1767+ }
1768+ sb .WriteString (htmltemplate .HTMLEscapeString (value [last :]))
1769+ return sb .String ()
1770+ }
1771+
1772+ // systemSummaryTableHTMLRenderer renders the System Summary table with special handling to highlight
1773+ // memory speed mismatches. It expects the "Installed Memory" and "System Summary" fields to contain
1774+ // values in the format of "<max speed> [<configured speed>]". If such a pattern is detected and the
1775+ // max speed differs from the configured speed, the configured speed will be highlighted in the HTML
1776+ // output.
1777+ func systemSummaryTableHTMLRenderer (tableValues table.TableValues , targetName string ) string {
1778+ values := [][]string {}
1779+ var tableValueStyles [][]string
1780+ for _ , field := range tableValues .Fields {
1781+ rowValues := []string {report .CreateFieldNameWithDescription (field .Name , field .Description )}
1782+ if len (field .Values ) > 0 {
1783+ fieldValue := field .Values [0 ]
1784+ if field .Name == "Installed Memory" || field .Name == "System Summary" {
1785+ rowValues = append (rowValues , highlightConfiguredSpeedMismatchesHTML (fieldValue ))
1786+ } else {
1787+ rowValues = append (rowValues , htmltemplate .HTMLEscapeString (fieldValue ))
1788+ }
1789+ } else {
1790+ rowValues = append (rowValues , "" )
1791+ }
1792+ values = append (values , rowValues )
1793+ tableValueStyles = append (tableValueStyles , []string {"font-weight:bold" })
1794+ }
1795+ _ = targetName
1796+ return report .RenderHTMLTable ([]string {}, values , "pure-table pure-table-striped" , tableValueStyles )
1797+ }
1798+
1799+ func systemSummaryTableHTMLMultiTargetRenderer (tableValues []table.TableValues , targetNames []string ) string {
1800+ if len (tableValues ) == 0 {
1801+ return ""
1802+ }
1803+
1804+ values := [][]string {}
1805+ var tableValueStyles [][]string
1806+ for fieldIndex , field := range tableValues [0 ].Fields {
1807+ rowValues := []string {report .CreateFieldNameWithDescription (field .Name , field .Description )}
1808+ for _ , targetTableValues := range tableValues {
1809+ if len (targetTableValues .Fields ) > fieldIndex && len (targetTableValues .Fields [fieldIndex ].Values ) > 0 {
1810+ fieldValue := targetTableValues .Fields [fieldIndex ].Values [0 ]
1811+ if field .Name == "Installed Memory" || field .Name == "System Summary" {
1812+ rowValues = append (rowValues , highlightConfiguredSpeedMismatchesHTML (fieldValue ))
1813+ } else {
1814+ rowValues = append (rowValues , htmltemplate .HTMLEscapeString (fieldValue ))
1815+ }
1816+ } else {
1817+ rowValues = append (rowValues , "" )
1818+ }
1819+ }
1820+ values = append (values , rowValues )
1821+ tableValueStyles = append (tableValueStyles , []string {"font-weight:bold" })
1822+ }
1823+
1824+ headers := []string {"" }
1825+ headers = append (headers , targetNames ... )
1826+ return report .RenderHTMLTable (headers , values , "pure-table pure-table-striped" , tableValueStyles )
1827+ }
1828+
1829+ // dimmDetailsHTML takes the DIMM details string and returns an HTML-escaped version of it with the
1830+ // configured speed highlighted if it differs from the maximum speed.
1831+ func dimmDetailsHTML (details string ) string {
1832+ if details == "No Module Installed" {
1833+ return htmltemplate .HTMLEscapeString (details )
1834+ }
1835+
1836+ matches := configuredSpeedPattern .FindAllStringSubmatchIndex (details , - 1 )
1837+ if len (matches ) == 0 {
1838+ return htmltemplate .HTMLEscapeString (details )
1839+ }
1840+ match := matches [len (matches )- 1 ]
1841+ if len (match ) != 6 || match [1 ] != len (details ) {
1842+ return htmltemplate .HTMLEscapeString (details )
1843+ }
1844+
1845+ maxSpeed := strings .TrimSpace (details [match [2 ]:match [3 ]])
1846+ configuredSpeed := strings .TrimSpace (details [match [4 ]:match [5 ]])
1847+ if maxSpeed == "" || configuredSpeed == "" || configuredSpeed == maxSpeed {
1848+ return htmltemplate .HTMLEscapeString (details )
1849+ }
1850+
1851+ // highlight configured speed when it differs from the DIMM maximum speed
1852+ return htmltemplate .HTMLEscapeString (details [:match [4 ]]) +
1853+ `<span style="background-color:#fff3b0;font-weight:700">` +
1854+ htmltemplate .HTMLEscapeString (details [match [4 ]:match [5 ]]) +
1855+ `</span>` +
1856+ htmltemplate .HTMLEscapeString (details [match [5 ]:])
1857+ }
1858+
17321859func dimmTableHTMLRenderer (tableValues table.TableValues , targetName string ) string {
17331860 if len (tableValues .Fields ) <= max (extract .DerivedSocketIdx , extract .DerivedChannelIdx , extract .DerivedSlotIdx ) ||
17341861 len (tableValues .Fields [extract .DerivedSocketIdx ].Values ) == 0 ||
@@ -1794,7 +1921,7 @@ func dimmTableHTMLRenderer(tableValues table.TableValues, targetName string) str
17941921 slotTableValuesStyles = append (slotTableValuesStyles , []string {})
17951922 for _ , slot := range slotKeys {
17961923 dimmDetails := channelMap [slot ]
1797- slotTableValues [0 ] = append (slotTableValues [0 ], htmltemplate . HTMLEscapeString (dimmDetails ))
1924+ slotTableValues [0 ] = append (slotTableValues [0 ], dimmDetailsHTML (dimmDetails ))
17981925 var slotColor string
17991926 if dimmDetails == "No Module Installed" {
18001927 slotColor = "background-color:silver"
0 commit comments