PageRenderTime 101ms CodeModel.GetById 11ms app.highlight 83ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llcommon/llprocessor.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 887 lines | 668 code | 123 blank | 96 comment | 88 complexity | 584a85bd935d6d2f20071a6af25844bb MD5 | raw file
  1/** 
  2 * @file llprocessor.cpp
  3 * @brief Code to figure out the processor. Originally by Benjamin Jurke.
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28#include "llprocessor.h"
 29
 30#include "llerror.h"
 31
 32//#include <memory>
 33
 34#if LL_WINDOWS
 35#	define WIN32_LEAN_AND_MEAN
 36#	include <winsock2.h>
 37#	include <windows.h>
 38#	define _interlockedbittestandset _renamed_interlockedbittestandset
 39#	define _interlockedbittestandreset _renamed_interlockedbittestandreset
 40#	include <intrin.h>
 41#	undef _interlockedbittestandset
 42#	undef _interlockedbittestandreset
 43#endif
 44
 45#include "llsd.h"
 46
 47#if LL_MSVC && _M_X64
 48#      define LL_X86_64 1
 49#      define LL_X86 1
 50#elif LL_MSVC && _M_IX86
 51#      define LL_X86 1
 52#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
 53#      define LL_X86_64 1
 54#      define LL_X86 1
 55#elif LL_GNUC && ( defined(__i386__) )
 56#      define LL_X86 1
 57#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
 58#      define LL_PPC 1
 59#endif
 60
 61class LLProcessorInfoImpl; // foward declaration for the mImpl;
 62
 63namespace 
 64{
 65	enum cpu_info
 66	{
 67		eBrandName = 0,
 68		eFrequency,
 69		eVendor,
 70		eStepping,
 71		eFamily,
 72		eExtendedFamily,
 73		eModel,
 74		eExtendedModel,
 75		eType,
 76		eBrandID,
 77		eFamilyName
 78	};
 79		
 80
 81	const char* cpu_info_names[] = 
 82	{
 83		"Processor Name",
 84		"Frequency",
 85		"Vendor",
 86		"Stepping",
 87		"Family",
 88		"Extended Family",
 89		"Model",
 90		"Extended Model",
 91		"Type",
 92		"Brand ID",
 93		"Family Name"
 94	};
 95
 96	enum cpu_config
 97	{
 98		eMaxID,
 99		eMaxExtID,
100		eCLFLUSHCacheLineSize,
101		eAPICPhysicalID,
102		eCacheLineSize,
103		eL2Associativity,
104		eCacheSizeK,
105		eFeatureBits,
106		eExtFeatureBits
107	};
108
109	const char* cpu_config_names[] =
110	{
111		"Max Supported CPUID level",
112		"Max Supported Ext. CPUID level",
113		"CLFLUSH cache line size",
114		"APIC Physical ID",
115		"Cache Line Size", 
116		"L2 Associativity",
117		"Cache Size",
118		"Feature Bits",
119		"Ext. Feature Bits"
120	};
121
122
123
124	// *NOTE:Mani - this contains the elements we reference directly and extensions beyond the first 32.
125	// The rest of the names are referenced by bit maks returned from cpuid.
126	enum cpu_features 
127	{
128		eSSE_Ext=25,
129		eSSE2_Ext=26,
130
131		eSSE3_Features=32,
132		eMONTIOR_MWAIT=33,
133		eCPLDebugStore=34,
134		eThermalMonitor2=35,
135		eAltivec=36
136	};
137
138	const char* cpu_feature_names[] =
139	{
140		"x87 FPU On Chip",
141		"Virtual-8086 Mode Enhancement",
142		"Debugging Extensions",
143		"Page Size Extensions",
144		"Time Stamp Counter",
145		"RDMSR and WRMSR Support",
146		"Physical Address Extensions",
147		"Machine Check Exception",
148		"CMPXCHG8B Instruction",
149		"APIC On Chip",
150		"Unknown1",
151		"SYSENTER and SYSEXIT",
152		"Memory Type Range Registers",
153		"PTE Global Bit",
154		"Machine Check Architecture",
155		"Conditional Move/Compare Instruction",
156		"Page Attribute Table",
157		"Page Size Extension",
158		"Processor Serial Number",
159		"CFLUSH Extension",
160		"Unknown2",
161		"Debug Store",
162		"Thermal Monitor and Clock Ctrl",
163		"MMX Technology",
164		"FXSAVE/FXRSTOR",
165		"SSE Extensions",
166		"SSE2 Extensions",
167		"Self Snoop",
168		"Hyper-threading Technology",
169		"Thermal Monitor",
170		"Unknown4",
171		"Pend. Brk. EN.", // 31 End of FeatureInfo bits
172
173		"SSE3 New Instructions", // 32
174		"MONITOR/MWAIT", 
175		"CPL Qualified Debug Store",
176		"Thermal Monitor 2",
177
178		"Altivec"
179	};
180
181	std::string intel_CPUFamilyName(int composed_family) 
182	{
183		switch(composed_family)
184		{
185		case 3: return "Intel i386";
186		case 4: return "Intel i486";
187		case 5: return "Intel Pentium";
188		case 6: return "Intel Pentium Pro/2/3, Core";
189		case 7: return "Intel Itanium (IA-64)";
190		case 0xF: return "Intel Pentium 4";
191		case 0x10: return "Intel Itanium 2 (IA-64)";
192		}
193		return "Unknown";
194	}
195	
196	std::string amd_CPUFamilyName(int composed_family) 
197	{
198		switch(composed_family)
199		{
200		case 4: return "AMD 80486/5x86";
201		case 5: return "AMD K5/K6";
202		case 6: return "AMD K7";
203		case 0xF: return "AMD K8";
204		case 0x10: return "AMD K8L";
205		}
206   		return "Unknown";
207	}
208
209	std::string compute_CPUFamilyName(const char* cpu_vendor, int composed_family) 
210	{
211		const char* intel_string = "GenuineIntel";
212		const char* amd_string = "AuthenticAMD";
213		if(!strncmp(cpu_vendor, intel_string, strlen(intel_string)))
214		{
215			return intel_CPUFamilyName(composed_family);
216		}
217		else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string)))
218		{
219			return amd_CPUFamilyName(composed_family);
220		}
221		return "Unknown";
222	}
223
224	std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family) 
225	{
226		const char* intel_string = "GenuineIntel";
227		const char* amd_string = "AuthenticAMD";
228		if(!strncmp(cpu_vendor, intel_string, strlen(intel_string)))
229		{
230			U32 composed_family = family + ext_family;
231			return intel_CPUFamilyName(composed_family);
232		}
233		else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string)))
234		{
235			U32 composed_family = (family == 0xF) 
236				? family + ext_family
237				: family;
238			return amd_CPUFamilyName(composed_family);
239		}
240		return "Unknown";
241	}
242
243} // end unnamed namespace
244
245// The base class for implementations.
246// Each platform should override this class.
247class LLProcessorInfoImpl
248{
249public:
250	LLProcessorInfoImpl() 
251	{
252		mProcessorInfo["info"] = LLSD::emptyMap();
253		mProcessorInfo["config"] = LLSD::emptyMap();
254		mProcessorInfo["extension"] = LLSD::emptyMap();		
255	}
256	virtual ~LLProcessorInfoImpl() {}
257
258	F64 getCPUFrequency() const 
259	{ 
260		return getInfo(eFrequency, 0).asReal(); 
261	}
262
263	bool hasSSE() const 
264	{ 
265		return hasExtension(cpu_feature_names[eSSE_Ext]);
266	}
267
268	bool hasSSE2() const
269	{ 
270		return hasExtension(cpu_feature_names[eSSE2_Ext]);
271	}
272
273	bool hasAltivec() const 
274	{
275		return hasExtension("Altivec"); 
276	}
277
278	std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unknown").asString(); }
279	std::string getCPUBrandName() const { return getInfo(eBrandName, "Unknown").asString(); }
280
281	// This is virtual to support a different linux format.
282	// *NOTE:Mani - I didn't want to screw up server use of this data...
283	virtual std::string getCPUFeatureDescription() const 
284	{
285		std::ostringstream out;
286		out << std::endl << std::endl;
287		out << "// CPU General Information" << std::endl;
288		out << "//////////////////////////" << std::endl;
289		out << "Processor Name:   " << getCPUBrandName() << std::endl;
290		out << "Frequency:        " << getCPUFrequency() << " MHz" << std::endl;
291		out << "Vendor:			  " << getInfo(eVendor, "Unknown").asString() << std::endl;
292		out << "Family:           " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl;
293		out << "Extended family:  " << getInfo(eExtendedFamily, 0) << std::endl;
294		out << "Model:            " << getInfo(eModel, 0) << std::endl;
295		out << "Extended model:   " << getInfo(eExtendedModel, 0) << std::endl;
296		out << "Type:             " << getInfo(eType, 0) << std::endl;
297		out << "Brand ID:         " << getInfo(eBrandID, 0) << std::endl;
298		out << std::endl;
299		out << "// CPU Configuration" << std::endl;
300		out << "//////////////////////////" << std::endl;
301		
302		// Iterate through the dictionary of configuration options.
303		LLSD configs = mProcessorInfo["config"];
304		for(LLSD::map_const_iterator cfgItr = configs.beginMap(); cfgItr != configs.endMap(); ++cfgItr)
305		{
306			out << cfgItr->first << " = " << cfgItr->second << std::endl;
307		}
308		out << std::endl;
309		
310		out << "// CPU Extensions" << std::endl;
311		out << "//////////////////////////" << std::endl;
312		
313		for(LLSD::map_const_iterator itr = mProcessorInfo["extension"].beginMap(); itr != mProcessorInfo["extension"].endMap(); ++itr)
314		{
315			out << "  " << itr->first << std::endl;			
316		}
317		return out.str(); 
318	}
319
320protected:
321	void setInfo(cpu_info info_type, const LLSD& value) 
322	{
323		setInfo(cpu_info_names[info_type], value);
324	}
325    LLSD getInfo(cpu_info info_type, const LLSD& defaultVal) const
326	{
327		return getInfo(cpu_info_names[info_type], defaultVal);
328	}
329
330	void setConfig(cpu_config config_type, const LLSD& value) 
331	{ 
332		setConfig(cpu_config_names[config_type], value);
333	}
334	LLSD getConfig(cpu_config config_type, const LLSD& defaultVal) const
335	{ 
336		return getConfig(cpu_config_names[config_type], defaultVal);
337	}
338
339	void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; }
340	bool hasExtension(const std::string& name) const
341	{ 
342		return mProcessorInfo["extension"].has(name);
343	}
344
345private:
346	void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; }
347	LLSD getInfo(const std::string& name, const LLSD& defaultVal) const
348	{ 
349		if(mProcessorInfo["info"].has(name))
350		{
351			return mProcessorInfo["info"][name];
352		}
353		return defaultVal;
354	}
355	void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; }
356	LLSD getConfig(const std::string& name, const LLSD& defaultVal) const
357	{ 
358		LLSD r = mProcessorInfo["config"].get(name);
359		return r.isDefined() ? r : defaultVal;
360	}
361
362private:
363
364	LLSD mProcessorInfo;
365};
366
367
368#ifdef LL_MSVC
369// LL_MSVC and not LLWINDOWS because some of the following code 
370// uses the MSVC compiler intrinsics __cpuid() and __rdtsc().
371
372// Delays for the specified amount of milliseconds
373static void _Delay(unsigned int ms)
374{
375	LARGE_INTEGER freq, c1, c2;
376	__int64 x;
377
378	// Get High-Res Timer frequency
379	if (!QueryPerformanceFrequency(&freq))	
380		return;
381
382	// Convert ms to High-Res Timer value
383	x = freq.QuadPart/1000*ms;		
384
385	// Get first snapshot of High-Res Timer value
386	QueryPerformanceCounter(&c1);		
387	do
388	{
389		// Get second snapshot
390		QueryPerformanceCounter(&c2);	
391	}while(c2.QuadPart-c1.QuadPart < x);
392	// Loop while (second-first < x)	
393}
394
395static F64 calculate_cpu_frequency(U32 measure_msecs)
396{
397	if(measure_msecs == 0)
398	{
399		return 0;
400	}
401
402	// After that we declare some vars and check the frequency of the high
403	// resolution timer for the measure process.
404	// If there"s no high-res timer, we exit.
405	unsigned __int64 starttime, endtime, timedif, freq, start, end, dif;
406	if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq))
407	{
408		return 0;
409	}
410
411	// Now we can init the measure process. We set the process and thread priority
412	// to the highest available level (Realtime priority). Also we focus the
413	// first processor in the multiprocessor system.
414	HANDLE hProcess = GetCurrentProcess();
415	HANDLE hThread = GetCurrentThread();
416	unsigned long dwCurPriorityClass = GetPriorityClass(hProcess);
417	int iCurThreadPriority = GetThreadPriority(hThread);
418	unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1;
419	GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask);
420
421	SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS);
422	SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
423	SetProcessAffinityMask(hProcess, dwNewMask);
424
425	//// Now we call a CPUID to ensure, that all other prior called functions are
426	//// completed now (serialization)
427	//__asm cpuid
428	int cpu_info[4] = {-1};
429	__cpuid(cpu_info, 0);
430
431	// We ask the high-res timer for the start time
432	QueryPerformanceCounter((LARGE_INTEGER *) &starttime);
433
434	// Then we get the current cpu clock and store it
435	start = __rdtsc();
436
437	// Now we wart for some msecs
438	_Delay(measure_msecs);
439	//	Sleep(uiMeasureMSecs);
440
441	// We ask for the end time
442	QueryPerformanceCounter((LARGE_INTEGER *) &endtime);
443
444	// And also for the end cpu clock
445	end = __rdtsc();
446
447	// Now we can restore the default process and thread priorities
448	SetProcessAffinityMask(hProcess, dwProcessMask);
449	SetThreadPriority(hThread, iCurThreadPriority);
450	SetPriorityClass(hProcess, dwCurPriorityClass);
451
452	// Then we calculate the time and clock differences
453	dif = end - start;
454	timedif = endtime - starttime;
455
456	// And finally the frequency is the clock difference divided by the time
457	// difference. 
458	F64 frequency = (F64)dif / (((F64)timedif) / freq);
459
460	// At last we just return the frequency that is also stored in the call
461	// member var uqwFrequency - converted to MHz
462	return frequency  / (F64)1000000;
463}
464
465// Windows implementation
466class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl
467{
468public:
469	LLProcessorInfoWindowsImpl()
470	{
471		getCPUIDInfo();
472		setInfo(eFrequency, calculate_cpu_frequency(50));
473	}
474
475private:
476	void getCPUIDInfo()
477	{
478		// http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx
479
480		// __cpuid with an InfoType argument of 0 returns the number of
481		// valid Ids in cpu_info[0] and the CPU identification string in
482		// the other three array elements. The CPU identification string is
483		// not in linear order. The code below arranges the information 
484		// in a human readable form.
485		int cpu_info[4] = {-1};
486		__cpuid(cpu_info, 0);
487		unsigned int ids = (unsigned int)cpu_info[0];
488		setConfig(eMaxID, (S32)ids);
489
490		char cpu_vendor[0x20];
491		memset(cpu_vendor, 0, sizeof(cpu_vendor));
492		*((int*)cpu_vendor) = cpu_info[1];
493		*((int*)(cpu_vendor+4)) = cpu_info[3];
494		*((int*)(cpu_vendor+8)) = cpu_info[2];
495		setInfo(eVendor, cpu_vendor);
496
497		// Get the information associated with each valid Id
498		for(unsigned int i=0; i<=ids; ++i)
499		{
500			__cpuid(cpu_info, i);
501
502			// Interpret CPU feature information.
503			if  (i == 1)
504			{
505				setInfo(eStepping, cpu_info[0] & 0xf);
506				setInfo(eModel, (cpu_info[0] >> 4) & 0xf);
507				int family = (cpu_info[0] >> 8) & 0xf;
508				setInfo(eFamily, family);
509				setInfo(eType, (cpu_info[0] >> 12) & 0x3);
510				setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf);
511				int ext_family = (cpu_info[0] >> 20) & 0xff;
512				setInfo(eExtendedFamily, ext_family);
513				setInfo(eBrandID, cpu_info[1] & 0xff);
514
515				setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family));
516
517				setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
518				setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
519				
520				if(cpu_info[2] & 0x1)
521				{
522					setExtension(cpu_feature_names[eSSE3_Features]);
523				}
524
525				if(cpu_info[2] & 0x8)
526				{
527					setExtension(cpu_feature_names[eMONTIOR_MWAIT]);
528				}
529				
530				if(cpu_info[2] & 0x10)
531				{
532					setExtension(cpu_feature_names[eCPLDebugStore]);
533				}
534				
535				if(cpu_info[2] & 0x100)
536				{
537					setExtension(cpu_feature_names[eThermalMonitor2]);
538				}
539						
540				unsigned int feature_info = (unsigned int) cpu_info[3];
541				for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1)
542				{
543					if(feature_info & bit)
544					{
545						setExtension(cpu_feature_names[index]);
546					}
547				}
548			}
549		}
550
551		// Calling __cpuid with 0x80000000 as the InfoType argument
552		// gets the number of valid extended IDs.
553		__cpuid(cpu_info, 0x80000000);
554		unsigned int ext_ids = cpu_info[0];
555		setConfig(eMaxExtID, 0);
556
557		char cpu_brand_string[0x40];
558		memset(cpu_brand_string, 0, sizeof(cpu_brand_string));
559
560		// Get the information associated with each extended ID.
561		for(unsigned int i=0x80000000; i<=ext_ids; ++i)
562		{
563			__cpuid(cpu_info, i);
564
565			// Interpret CPU brand string and cache information.
566			if  (i == 0x80000002)
567				memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info));
568			else if  (i == 0x80000003)
569				memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info));
570			else if  (i == 0x80000004)
571			{
572				memcpy(cpu_brand_string + 32, cpu_info, sizeof(cpu_info));
573				setInfo(eBrandName, cpu_brand_string);
574			}
575			else if  (i == 0x80000006)
576			{
577				setConfig(eCacheLineSize, cpu_info[2] & 0xff);
578				setConfig(eL2Associativity, (cpu_info[2] >> 12) & 0xf);
579				setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff);
580			}
581		}
582	}
583};
584
585#elif LL_DARWIN
586
587#include <mach/machine.h>
588#include <sys/sysctl.h>
589
590class LLProcessorInfoDarwinImpl : public LLProcessorInfoImpl
591{
592public:
593	LLProcessorInfoDarwinImpl() 
594	{
595		getCPUIDInfo();
596		uint64_t frequency = getSysctlInt64("hw.cpufrequency");
597		setInfo(eFrequency, (F64)frequency  / (F64)1000000);
598	}
599
600	virtual ~LLProcessorInfoDarwinImpl() {}
601
602private:
603	int getSysctlInt(const char* name)
604   	{
605		int result = 0;
606		size_t len = sizeof(int);
607		int error = sysctlbyname(name, (void*)&result, &len, NULL, 0);
608		return error == -1 ? 0 : result;
609   	}
610
611	uint64_t getSysctlInt64(const char* name)
612   	{
613		uint64_t value = 0;
614		size_t size = sizeof(value);
615		int result = sysctlbyname(name, (void*)&value, &size, NULL, 0);
616		if ( result == 0 ) 
617		{ 
618			if ( size == sizeof( uint64_t ) ) 
619				; 
620			else if ( size == sizeof( uint32_t ) ) 
621				value = (uint64_t)(( uint32_t *)&value); 
622			else if ( size == sizeof( uint16_t ) ) 
623				value =  (uint64_t)(( uint16_t *)&value); 
624			else if ( size == sizeof( uint8_t ) ) 
625				value =  (uint64_t)(( uint8_t *)&value); 
626			else
627			{
628				LL_WARNS("Unknown type returned from sysctl!") << LL_ENDL;
629			}
630		}
631				
632		return result == -1 ? 0 : value;
633   	}
634	
635	void getCPUIDInfo()
636	{
637		size_t len = 0;
638
639		char cpu_brand_string[0x40];
640		len = sizeof(cpu_brand_string);
641		memset(cpu_brand_string, 0, len);
642		sysctlbyname("machdep.cpu.brand_string", (void*)cpu_brand_string, &len, NULL, 0);
643		cpu_brand_string[0x3f] = 0;
644		setInfo(eBrandName, cpu_brand_string);
645		
646		char cpu_vendor[0x20];
647		len = sizeof(cpu_vendor);
648		memset(cpu_vendor, 0, len);
649		sysctlbyname("machdep.cpu.vendor", (void*)cpu_vendor, &len, NULL, 0);
650		cpu_vendor[0x1f] = 0;
651		setInfo(eVendor, cpu_vendor);
652
653		setInfo(eStepping, getSysctlInt("machdep.cpu.stepping"));
654		setInfo(eModel, getSysctlInt("machdep.cpu.model"));
655		int family = getSysctlInt("machdep.cpu.family");
656		int ext_family = getSysctlInt("machdep.cpu.extfamily");
657		setInfo(eFamily, family);
658		setInfo(eExtendedFamily, ext_family);
659		setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family));
660		setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel"));
661		setInfo(eBrandID, getSysctlInt("machdep.cpu.brand"));
662		setInfo(eType, 0); // ? where to find this?
663
664		//setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
665		//setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
666		setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize"));
667		setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity"));
668		setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size"));
669		
670		uint64_t feature_info = getSysctlInt64("machdep.cpu.feature_bits");
671		S32 *feature_infos = (S32*)(&feature_info);
672		
673		setConfig(eFeatureBits, feature_infos[0]);
674
675		for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1)
676		{
677			if(feature_info & bit)
678			{
679				setExtension(cpu_feature_names[index]);
680			}
681		}
682
683		// *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be
684		// The feature bits I think it is. Here's a test:
685#ifndef LL_RELEASE_FOR_DOWNLOAD
686	#if defined(__i386__) && defined(__PIC__)
687			/* %ebx may be the PIC register.  */
688		#define __cpuid(level, a, b, c, d)			\
689		__asm__ ("xchgl\t%%ebx, %1\n\t"			\
690				"cpuid\n\t"					\
691				"xchgl\t%%ebx, %1\n\t"			\
692				: "=a" (a), "=r" (b), "=c" (c), "=d" (d)	\
693				: "0" (level))
694	#else
695		#define __cpuid(level, a, b, c, d)			\
696		__asm__ ("cpuid\n\t"					\
697				 : "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
698				 : "0" (level))
699	#endif
700
701		unsigned int eax, ebx, ecx, edx;
702		__cpuid(0x1, eax, ebx, ecx, edx);
703		if(feature_infos[0] != (S32)edx)
704		{
705			llerrs << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << llendl;
706		} 
707#endif // LL_RELEASE_FOR_DOWNLOAD 	
708
709
710		uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits");
711		S32 *ext_feature_infos = (S32*)(&ext_feature_info);
712		setConfig(eExtFeatureBits, ext_feature_infos[0]);
713	}
714};
715
716#elif LL_LINUX
717const char CPUINFO_FILE[] = "/proc/cpuinfo";
718
719class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl
720{
721public:
722	LLProcessorInfoLinuxImpl() 
723	{
724		get_proc_cpuinfo();
725	}
726
727	virtual ~LLProcessorInfoLinuxImpl() {}
728private:
729
730	void get_proc_cpuinfo()
731	{
732		std::map< std::string, std::string > cpuinfo;
733		LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb");
734		if(cpuinfo_fp)
735		{
736			char line[MAX_STRING];
737			memset(line, 0, MAX_STRING);
738			while(fgets(line, MAX_STRING, cpuinfo_fp))
739			{
740				// /proc/cpuinfo on Linux looks like:
741				// name\t*: value\n
742				char* tabspot = strchr( line, '\t' );
743				if (tabspot == NULL)
744					continue;
745				char* colspot = strchr( tabspot, ':' );
746				if (colspot == NULL)
747					continue;
748				char* spacespot = strchr( colspot, ' ' );
749				if (spacespot == NULL)
750					continue;
751				char* nlspot = strchr( line, '\n' );
752				if (nlspot == NULL)
753					nlspot = line + strlen( line ); // Fallback to terminating NUL
754				std::string linename( line, tabspot );
755				std::string llinename(linename);
756				LLStringUtil::toLower(llinename);
757				std::string lineval( spacespot + 1, nlspot );
758				cpuinfo[ llinename ] = lineval;
759			}
760			fclose(cpuinfo_fp);
761		}
762# if LL_X86
763
764// *NOTE:Mani - eww, macros! srry.
765#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \
766		if (!cpuinfo[cpuinfo_id].empty()) \
767		{ setInfo(llpi_id, cpuinfo[cpuinfo_id]);}
768
769#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \
770		{\
771			S32 result; \
772			if (!cpuinfo[cpuinfo_id].empty() \
773				&& LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result))	\
774		    { setInfo(llpi_id, result);} \
775		}
776		
777		F64 mhz;
778		if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz)
779			&& 200.0 < mhz && mhz < 10000.0)
780		{
781		    setInfo(eFrequency,(F64)(mhz));
782		}
783
784		LLPI_SET_INFO_STRING(eBrandName, "model name");		
785		LLPI_SET_INFO_STRING(eVendor, "vendor_id");
786
787		LLPI_SET_INFO_INT(eStepping, "stepping");
788		LLPI_SET_INFO_INT(eModel, "model");
789
790		
791		S32 family;							 
792		if (!cpuinfo["cpu family"].empty() 
793			&& LLStringUtil::convertToS32(cpuinfo["cpu family"], family))	
794		{ 
795			setInfo(eFamily, family);
796		}
797 
798		setInfo(eFamilyName, compute_CPUFamilyName(cpuinfo["vendor_id"].c_str(), family));
799
800		// setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel"));
801		// setInfo(eBrandID, getSysctlInt("machdep.cpu.brand"));
802		// setInfo(eType, 0); // ? where to find this?
803
804		//setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
805		//setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
806		//setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize"));
807		//setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity"));
808		//setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size"));
809		
810		// Read extensions
811		std::string flags = " " + cpuinfo["flags"] + " ";
812		LLStringUtil::toLower(flags);
813
814		if( flags.find( " sse " ) != std::string::npos )
815		{
816			setExtension(cpu_feature_names[eSSE_Ext]); 
817		}
818
819		if( flags.find( " sse2 " ) != std::string::npos )
820		{
821			setExtension(cpu_feature_names[eSSE2_Ext]);
822		}
823	
824# endif // LL_X86
825	}
826
827	std::string getCPUFeatureDescription() const 
828	{
829		std::ostringstream s;
830
831		// *NOTE:Mani - This is for linux only.
832		LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb");
833		if(cpuinfo)
834		{
835			char line[MAX_STRING];
836			memset(line, 0, MAX_STRING);
837			while(fgets(line, MAX_STRING, cpuinfo))
838			{
839				line[strlen(line)-1] = ' ';
840				s << line;
841				s << std::endl;
842			}
843			fclose(cpuinfo);
844			s << std::endl;
845		}
846		else
847		{
848			s << "Unable to collect processor information" << std::endl;
849		}
850		return s.str();
851	}
852		
853};
854
855
856#endif // LL_MSVC elif LL_DARWIN elif LL_LINUX
857
858//////////////////////////////////////////////////////
859// Interface definition
860LLProcessorInfo::LLProcessorInfo() : mImpl(NULL)
861{
862	// *NOTE:Mani - not thread safe.
863	if(!mImpl)
864	{
865#ifdef LL_MSVC
866		static LLProcessorInfoWindowsImpl the_impl; 
867		mImpl = &the_impl;
868#elif LL_DARWIN
869		static LLProcessorInfoDarwinImpl the_impl; 
870		mImpl = &the_impl;
871#else
872		static LLProcessorInfoLinuxImpl the_impl; 
873		mImpl = &the_impl;		
874#endif // LL_MSVC
875	}
876}
877
878
879LLProcessorInfo::~LLProcessorInfo() {}
880F64 LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
881bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); }
882bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); }
883bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); }
884std::string LLProcessorInfo::getCPUFamilyName() const { return mImpl->getCPUFamilyName(); }
885std::string LLProcessorInfo::getCPUBrandName() const { return mImpl->getCPUBrandName(); }
886std::string LLProcessorInfo::getCPUFeatureDescription() const { return mImpl->getCPUFeatureDescription(); }
887