@@ -28,6 +28,12 @@ export type MemoryDevice = {
2828 /** Memory type (e.g., DDR4, LPDDR4X). */
2929 type ?: string ;
3030
31+ /** Name assigned to slot by the firmware. */
32+ locator ?: string ;
33+
34+ /** Name of the slot based on motherboard layout (bank locator). */
35+ bankName ?: string ;
36+
3137 /** Configured clock speed in MHz. */
3238 clockSpeed ?: number ;
3339
@@ -82,11 +88,80 @@ export async function getMemoryInfo(): Promise<Memory> {
8288 percentage : ( os . totalmem ( ) - os . freemem ( ) ) / os . totalmem ( ) ,
8389 } ,
8490 } ;
91+ const interleavePositions = new Set < number > ( ) ;
8592
8693 switch ( process . platform ) {
8794
95+ case 'darwin' :
8896 case 'linux' : {
89- // TODO
97+ const output = await execCommand (
98+ 'dmidecode --type memory'
99+ ) . catch ( ( ) => '' ) ;
100+ const lines = output . split ( '\n' ) ;
101+ let currDevice : MemoryDevice | null = null ;
102+ let ignoreBlock = true ;
103+ for ( let i = 0 ; i < lines . length ; i ++ ) {
104+ const [ key , value ] = lines [ i ] . split ( ': ' ) . map ( s => s . trim ( ) ) ;
105+ if ( ! key || ! currDevice ) { // empty line marks new device
106+ if ( currDevice && Object . keys ( currDevice ) . length > 0 ) {
107+ memoryInfo . devices = memoryInfo . devices || [ ] ;
108+ memoryInfo . devices . push ( currDevice as any ) ;
109+ }
110+ currDevice = { } ;
111+ ignoreBlock = true ;
112+ continue ;
113+ }
114+ if ( ! value ) {
115+ ignoreBlock = ignoreBlock || ( key === 'Memory Device' ) ;
116+ continue ; // skip lines without value
117+ }
118+ if ( ignoreBlock ) continue ; // skip block if marked to ignore
119+ if ( key === 'Total Width' || key === 'Data Width' ) {
120+ const busWidth = parseInt ( value . split ( ' ' ) [ 0 ] , 10 ) ; // strip unit 'bits'
121+ if ( ! Number . isNaN ( busWidth ) ) currDevice . busWidth = busWidth ;
122+ } else
123+ if ( key === 'Size' ) {
124+ const size = parseInt ( value . split ( ' ' ) [ 0 ] , 10 ) ; // strip unit 'MB'
125+ if ( ! Number . isNaN ( size ) ) currDevice . size = size * 1024 * 1024 ; // convert MB to bytes
126+ } else
127+ if ( key === 'Locator' || key === 'Socket Designation' ) {
128+ currDevice . locator = value ;
129+ } else
130+ if ( key === 'Bank Locator' ) {
131+ currDevice . bankName = value ;
132+ } else
133+ if ( key === 'Type' ) {
134+ currDevice . type = value ;
135+ } else
136+ if ( key === 'Speed' ) {
137+ const mhz = parseInt ( value . split ( ' ' ) [ 0 ] , 10 ) ; // strip unit 'MHz'
138+ if ( ! Number . isNaN ( mhz ) ) {
139+ currDevice . clockSpeed = mhz ;
140+ currDevice . maxClockSpeed = mhz ; // use max speed as default clock speed
141+ }
142+ } else
143+ if ( key === 'Configured Memory Speed' || key === 'Configured Clock Speed' ) {
144+ const mhz = parseInt ( value . split ( ' ' ) [ 0 ] , 10 ) ; // strip unit 'MHz'
145+ if ( ! Number . isNaN ( mhz ) ) {
146+ currDevice . clockSpeed = mhz ;
147+ if ( ! currDevice . maxClockSpeed ) currDevice . maxClockSpeed = mhz ; // use configured speed as default max speed
148+ }
149+ } else
150+ if ( key === 'Manufacturer' ) {
151+ currDevice . manufacturer = value ;
152+ } else
153+ if ( key === 'Part Number' ) {
154+ currDevice . model = currDevice . model || value ; // use first available model
155+ } else
156+ if ( key === 'Rank' ) {
157+ const rank = parseInt ( value , 10 ) ;
158+ if ( ! Number . isNaN ( rank ) ) interleavePositions . add ( rank ) ;
159+ } else
160+ if ( key === 'Voltage' || key === 'Configured Voltage' ) {
161+ const voltage = parseFloat ( value . split ( ' ' ) [ 0 ] ) ; // strip unit 'V'
162+ if ( ! Number . isNaN ( voltage ) ) currDevice . voltage = voltage ; // keep in volts
163+ }
164+ }
90165 break ;
91166 }
92167
@@ -95,7 +170,6 @@ export async function getMemoryInfo(): Promise<Memory> {
95170 'powershell -Command "Get-CimInstance -ClassName Win32_PhysicalMemory | Format-List"'
96171 ) . catch ( ( ) => '' ) ;
97172 const lines = output . split ( '\n' ) ;
98- const interleavePositions = new Set < number > ( ) ;
99173 let currDevice : MemoryDevice | null = null ;
100174 for ( let i = 0 ; i < lines . length ; i ++ ) {
101175 const [ key , value ] = lines [ i ] . split ( ' : ' ) . map ( s => s . trim ( ) ) ;
@@ -115,6 +189,9 @@ export async function getMemoryInfo(): Promise<Memory> {
115189 if ( key === 'Model' || key === 'PartNumber' ) {
116190 currDevice . model = currDevice . model || value ;
117191 } else
192+ if ( key === 'BankLabel' ) {
193+ currDevice . bankName = value ;
194+ } else
118195 if ( key === 'Capacity' ) {
119196 const size = parseInt ( value , 10 ) ;
120197 if ( ! Number . isNaN ( size ) ) currDevice . size = size ;
@@ -134,7 +211,7 @@ export async function getMemoryInfo(): Promise<Memory> {
134211 if ( ! currDevice . clockSpeed ) currDevice . clockSpeed = mhz ; // use max speed as default clock speed
135212 }
136213 } else
137- if ( key === 'ConfiguredClockSpeed' ) { // higher priority than Speed
214+ if ( key === 'ConfiguredClockSpeed' || key === 'ConfiguredMemorySpeed' ) { // higher priority than Speed
138215 const mhz = parseInt ( value , 10 ) ;
139216 if ( ! Number . isNaN ( mhz ) ) {
140217 currDevice . clockSpeed = mhz ;
@@ -145,6 +222,9 @@ export async function getMemoryInfo(): Promise<Memory> {
145222 const voltage = parseFloat ( value ) ;
146223 if ( ! Number . isNaN ( voltage ) ) currDevice . voltage = voltage / 1000 ; // convert from mV to V
147224 } else
225+ if ( key === 'DeviceLocator' ) {
226+ currDevice . locator = value ;
227+ } else
148228 if ( key === 'SMBIOSMemoryType' ) {
149229 switch ( value ) {
150230 case '0' : currDevice . type = 'Unknown' ; break ;
@@ -185,26 +265,26 @@ export async function getMemoryInfo(): Promise<Memory> {
185265 }
186266 }
187267 }
268+ break ;
269+ }
270+ }
188271
189- // post-processing
190- if ( memoryInfo . devices ) {
191- const parallelism = Math . max ( 1 , interleavePositions . size ) ;
192- let clockSpeed : number | undefined ;
193- let busWidth : number | undefined ;
194- let transfersPerCycle : number | undefined ;
195- for ( let device of memoryInfo . devices ) {
196- if ( device . size && device . busWidth && device . clockSpeed ) {
197- clockSpeed = clockSpeed ? Math . min ( clockSpeed , device . clockSpeed ) : device . clockSpeed ;
198- busWidth = busWidth ? Math . min ( busWidth , device . busWidth ) : device . busWidth ;
199- transfersPerCycle = transfersPerCycle ? Math . min ( transfersPerCycle , device . transfersPerClockCycle ?? 1 ) : device . transfersPerClockCycle ;
200- device . bandwidth = device . clockSpeed * 1000 * 1000 * device . busWidth / 8 * ( transfersPerCycle ?? 1 ) ;
201- }
202- }
203- if ( clockSpeed && busWidth )
204- memoryInfo . bandwidth = clockSpeed * 1000 * 1000 * busWidth / 8 * ( transfersPerCycle ?? 1 ) * parallelism ; // bytes per second
272+ // post-processing
273+ if ( memoryInfo . devices ) {
274+ const parallelism = Math . max ( 1 , interleavePositions . size ) ;
275+ let clockSpeed : number | undefined ;
276+ let busWidth : number | undefined ;
277+ let transfersPerCycle : number | undefined ;
278+ for ( let device of memoryInfo . devices ) {
279+ if ( device . size && device . busWidth && device . clockSpeed ) {
280+ clockSpeed = clockSpeed ? Math . min ( clockSpeed , device . clockSpeed ) : device . clockSpeed ;
281+ busWidth = busWidth ? Math . min ( busWidth , device . busWidth ) : device . busWidth ;
282+ transfersPerCycle = transfersPerCycle ? Math . min ( transfersPerCycle , device . transfersPerClockCycle ?? 1 ) : device . transfersPerClockCycle ;
283+ device . bandwidth = device . clockSpeed * 1000 * 1000 * device . busWidth / 8 * ( transfersPerCycle ?? 1 ) ;
205284 }
206- break ;
207285 }
286+ if ( clockSpeed && busWidth )
287+ memoryInfo . bandwidth = clockSpeed * 1000 * 1000 * busWidth / 8 * ( transfersPerCycle ?? 1 ) * parallelism ; // bytes per second
208288 }
209289
210290 return memoryInfo ;
0 commit comments