@@ -53,6 +53,9 @@ type HardwareProfile struct {
5353
5454// FormatHardwareProfile returns a human-readable summary of the hardware profile.
5555func FormatHardwareProfile (s * HardwareProfile ) string {
56+ if s == nil {
57+ return ""
58+ }
5659 var b strings.Builder
5760 if s .ProductName != "" {
5861 _ , _ = fmt .Fprintf (& b , " Product: %s\n " , s .ProductName )
@@ -63,6 +66,26 @@ func FormatHardwareProfile(s *HardwareProfile) string {
6366 if s .RAMBytes != nil {
6467 _ , _ = fmt .Fprintf (& b , " RAM: %.1f GB\n " , float64 (* s .RAMBytes )/ (1024 * 1024 * 1024 ))
6568 }
69+ parseGPUs (s , b )
70+ _ , _ = fmt .Fprintf (& b , " Arch: %s\n " , s .Architecture )
71+ if s .OS != "" || s .OSVersion != "" {
72+ _ , _ = fmt .Fprintf (& b , " OS: %s %s\n " , s .OS , s .OSVersion )
73+ }
74+ parseInterconnects (s , b )
75+ for _ , st := range s .Storage {
76+ _ , _ = fmt .Fprintf (& b , " Storage: %.1f GB" , float64 (st .StorageBytes )/ (1024 * 1024 * 1024 ))
77+ if st .StorageType != "" {
78+ _ , _ = fmt .Fprintf (& b , " (%s)" , st .StorageType )
79+ }
80+ if st .Name != "" {
81+ _ , _ = fmt .Fprintf (& b , " [%s]" , st .Name )
82+ }
83+ b .WriteString ("\n " )
84+ }
85+ return b .String ()
86+ }
87+
88+ func parseGPUs (s * HardwareProfile , b strings.Builder ) {
6689 for _ , gpu := range s .GPUs {
6790 if gpu .MemoryBytes != nil {
6891 memGB := float64 (* gpu .MemoryBytes ) / (1024 * 1024 * 1024 )
@@ -75,12 +98,19 @@ func FormatHardwareProfile(s *HardwareProfile) string {
7598 }
7699 b .WriteString ("\n " )
77100 }
78- _ , _ = fmt .Fprintf (& b , " Arch: %s\n " , s .Architecture )
79- if s .OS != "" || s .OSVersion != "" {
80- _ , _ = fmt .Fprintf (& b , " OS: %s %s\n " , s .OS , s .OSVersion )
81- }
101+ }
102+
103+ func parseInterconnects (s * HardwareProfile , b strings.Builder ) {
82104 for _ , ic := range s .Interconnects {
83105 _ , _ = fmt .Fprintf (& b , " Link: %s" , ic .Type )
106+ if ic .Generation > 0 || ic .Width > 0 {
107+ if ic .Generation > 0 {
108+ _ , _ = fmt .Fprintf (& b , " Gen%d" , ic .Generation )
109+ }
110+ if ic .Width > 0 {
111+ _ , _ = fmt .Fprintf (& b , " x%d" , ic .Width )
112+ }
113+ }
84114 if ic .Device != "" {
85115 _ , _ = fmt .Fprintf (& b , " (%s)" , ic .Device )
86116 }
@@ -89,17 +119,6 @@ func FormatHardwareProfile(s *HardwareProfile) string {
89119 }
90120 b .WriteString ("\n " )
91121 }
92- for _ , st := range s .Storage {
93- _ , _ = fmt .Fprintf (& b , " Storage: %.1f GB" , float64 (st .StorageBytes )/ (1024 * 1024 * 1024 ))
94- if st .StorageType != "" {
95- _ , _ = fmt .Fprintf (& b , " (%s)" , st .StorageType )
96- }
97- if st .Name != "" {
98- _ , _ = fmt .Fprintf (& b , " [%s]" , st .Name )
99- }
100- b .WriteString ("\n " )
101- }
102- return b .String ()
103122}
104123
105124// --- Content-parsing helpers (pure functions, used by Linux adapter and tests) ---
@@ -120,6 +139,8 @@ func parseCPUCountContent(content string) (int, error) {
120139}
121140
122141// parseMemInfoContent parses the content of /proc/meminfo.
142+ // Not used by the current Linux profiler (which uses syscall.Sysinfo), but
143+ // retained as a tested pure-function fallback for environments without sysinfo.
123144func parseMemInfoContent (content string ) (int64 , error ) {
124145 scanner := bufio .NewScanner (strings .NewReader (content ))
125146 for scanner .Scan () {
@@ -163,6 +184,8 @@ func unquote(s string) string {
163184
164185// parseStorageOutput parses lsblk output (NAME,SIZE,TYPE,ROTA columns),
165186// returning one StorageDevice entry per disk device. ROTA=0 → SSD, ROTA=1 → HDD.
187+ // Not used by the current Linux profiler (which uses probeStorageSysfs), but
188+ // retained as a tested pure-function fallback for environments without sysfs.
166189func parseStorageOutput (output string ) []StorageDevice {
167190 var devices []StorageDevice
168191 scanner := bufio .NewScanner (strings .NewReader (output ))
0 commit comments