Project:Alpha/Porting guide
This guide is intended to show C programmers how to port programs written on other architectures to the Alpha architecture. The overview has some basic information that should be known when porting any application to Alpha. The subsequent sections will have example problems, explanations, and solutions. All of the code examples compile and work without warnings on i386 systems, but fail or are potentially unsafe on alpha.
Overview
Endianness
Linux/Alpha is little endian
.
Word size
Alpha is a 64-bit platform. Alpha systems use a 64-bit kernel and a 64-bit userland.
Variable sizes (in bytes)
The most important things to note here are that the size of int
is not the same as the size of any of the pointer types and that the memory must be naturally aligned.
Type | Size | Alignment | Type | Size | Alignment |
---|---|---|---|---|---|
char | 1 | Any | char* | 8 | 8 |
void | 1 | Any | void* | 8 | 8 |
short | 2 | 2 | short* | 8 | 8 |
float | 4 | 4 | float* | 8 | 8 |
int | 4 | 4 | int* | 8 | 8 |
double | 8 | 8 | double* | 8 | 8 |
long double | 8 | 8 | long double* | 8 | 8 |
long | 8 | 8 | long* | 8 | 8 |
long long | 8 | 8 | long long* | 8 | 8 |
- sizes are the same for signed and unsigned variables.
Storage size mismatch
Identifying the problem
You will see compile time warnings about casting between types of different sizes.
tcort@topcat ~ $ gcc test.c
test.c: In function `main':
test.c:6: warning: cast to pointer from integer of different size
tcort@topcat ~ $
tcort@topcat ~ $ gcc test.c
test.c: In function `main':
test.c:10: warning: cast from pointer to integer of different size
tcort@topcat ~ $
Explanation of the problem
When you store a value in a variable of one size and convert it to another size, bits either get added or chopped from the original value. A lot of programmers assume that integers and pointers are the same size and cast freely between the two. While it works on x86 systems, it can cause problems on systems where the size of an integer is different than the size of a pointer.
Below is an example of a cast to pointer from integer of different size. This cast is dangerous because we are casting a 64-bit pointer to a 32 bit integer. We effectively chop off the 32 most significant bits, then we try to access the memory at that 64-bit address using only the 32 least significant bits. This will cause a segfault unless your program is located in physical memory between address 0x0000000000000000
and address 0x00000000FFFFFFFF
.
int main(void) {
/* Expected output: "Ice cream has no bones." */
/* Actual output: "Segmentation fault" */
int x = (int) "Ice cream has no bones.\n";
printf((const char*)x);
return 0;
}
Below is an example of a cast from pointer to integer of different size. Usually this is done for pointer arithmetic. Most of the time the pointers we use are relatively close to each other and they have a good position in memory, so the program executes normally. However, problems occur when the calculations require more than 32 bits. The example below would seg fault if the '!'
was located at 0x000000010000000e
and the array started at 0x00000000FFFFFFFF
.
int main(void) {
/* Expected output: "Banana Phone" */
/* Actual output: "Banana Phone" */
char c[16], *ptr;
strncpy(c,"Banana Phone\n!",16);
ptr = strchr(c,'!'); /* remove '!' by putting */
c[(int)ptr-(int)c]=0x00; /* '\0' in its place */
printf("%s",c);
return 0;
}
Finding a solution
When programming, don't try to be smart and do tricks with pointers. Always use the appropriate type, and don't cast between pointer and non-pointer types. Following that advice creates more portable code, and the code produced is generally easier to read.
int main(void) {
char *x = "Ice cream has no bones.\n";
printf(x);
return 0;
}
When doing pointer arithmetic, you should avoid casting between pointer and non-pointer types as much as possible.
int main(void) {
char c[16], *ptr;
strncpy(c,"Banana Phone\n!",16);
ptr = strchr(c,'!'); /* remove '!' by putting */
*(c+(ptr-c))=0x00; /* '\0' in its place */
printf("%s",c);
return 0;
}
The above example is used to demonstrate pointer arithmetic without casting. Removing the
'!'
character can be done by *ptr=0x00;
instead of doing *(c+(ptr-c))=0x00;
.Unaligned accesses
Identifying the problem
The kernel will log an unaligned trap.
Mar 14 22:07:25 [kernel] a.out(3572): unaligned trap at 0000000120000590: 0000000120010b52 2c 31
Explanation of the problem
On Alpha, all memory accesses have to be naturally aligned. For example, the address of a 4 byte integer must be a multiple of 4. The address of an 8 byte (long) integer must be a multiple of 8. The compiler will do its best to align variables automatically, but sometimes you can override this by doing some pointer tricks or adding assembly language code.
When a program does an unaligned memory access, the CPU traps into the kernel. The kernel then does an aligned access and returns the unaligned portion of memory that the program requested. The program continues to function normally as if nothing has happened.
Even though the program will work correctly with unaligned accesses, you should try to remove as many unaligned accesses as possible because they cause a performance hit.
The following example shows how to make an unaligned memory access by manipulating pointers.
int main(void) {
/* Expected results: Nothing appended to the kernel log */
/* Actual results: The following appended to the kernel log
* unaligned trap at 0000000120000590: 0000000120010b52 2c 31
*/
short x[10];
int *y = (int*) (x+1);
*y = 0;
return 0;
}
Finding a solution
The first step in fixing unaligned traps is determining where the unaligned access happens in our code. To do that we are going to use the following tool.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#ifndef __linux__
#include <sys/sysinfo.h>
#else
#include <asm/sysinfo.h>
#include <asm/unistd.h>
static int setsysinfo(unsigned long op, void *buffer, unsigned long size,
int *start, void *arg, unsigned long flag) {
syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag);
}
#endif
static void usage(void) {
fprintf(stderr, "usage: unaligned <command-path> [command-args...]\n\n"
" This program is designed to assist debugging of\n"
" unaligned traps by running the program in gdb\n"
" and causing it to get SIGBUS when it encounters\n"
" an unaligned trap.\n\n"
" It is free software written by Sean Hunter >sean@uncarved.co.uk<\n"
" based on code by Richard Henderson and Andrew Morgan. It was further\n"
" modified by Thomas Cort <tcort@cs.ubishops.ca>. It is provided\n"
" under the gnu public license without warrantees of any kind.\n\n");
exit(1);
}
void trap_unaligned(void) {
unsigned int buf[2];
buf[0] = SSIN_UACPROC;
buf[1] = UAC_SIGBUS | UAC_NOPRINT;
setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0);
}
int main(int argc, char **argv) {
char *tmp_filename;
char* my_debugger = "/usr/bin/gdb";
FILE* tmp_file;
int curr_arg, pid, status;
/* check that we have at least 1 argument */
if (argc < 2) {
usage();
}
trap_unaligned();
/* add the extra args to a file to pass to gdb */
tmp_filename = tmpnam(NULL);
tmp_file = fopen(tmp_filename, "w+");
if (!tmp_file) {
fprintf(stderr, "Unable to create temp file %s reason: %s\n",
tmp_filename,
strerror(errno));
exit(1);
}
fprintf(tmp_file, "file %s\n", argv[1]);
fprintf(tmp_file, "set args");
for(curr_arg = 2; curr_arg < argc; curr_arg++) {
fprintf(tmp_file, " %s", argv[curr_arg]);
}
fprintf(tmp_file, "\n");
fprintf(tmp_file, "run\n");
fclose(tmp_file);
printf("Extra arguments passed to gdb in file %s.\n", tmp_filename);
if ((pid = fork()) < 0) {
fprintf(stderr, "fork() failed!");
exit(1);
}
if (pid == 0) /*child*/ {
execlp(my_debugger, argv[1], "-x", tmp_filename, NULL);
/* if we fall through to here, our exec failed -- announce the fact */
fprintf(stderr, "Unable to execute command: %s\n", strerror(errno));
usage();
}
while (wait(&status) != pid);
unlink(tmp_filename); /* clean up */
free(tmp_filename);
}
Using this tool is very simple. Compile the code above. Then, compile your program with debugging information via the -g3
compiler flag. Finally, execute unalign
with the name of your program as an argument.
tcort@topcat ~ $ gcc unalign.c -o unalign
tcort@topcat ~ $ gcc test.c -g3
tcort@topcat ~ $ ./unalign a.out
Extra arguments passed to gdb in file /tmp/fileKAaEhf.
GNU gdb 6.4
Copyright 2005 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "alpha-unknown-linux-gnu".
Using host libthread_db library "/lib/libthread_db.so.1".
Program received signal SIGBUS, Bus error.
0x00000001200005cc in main () at test.c:11
11 *y = 0;
(gdb) The program is running. Exit anyway? (y or n) y
Bus error
Another approach is to use sys-apps/prctl
package which uses prctl(2)
syscall for the same purpose. Make sure you have at least linux-2.6.22
for it.
$ prctl --unaligned=signal gdb --args ./u
(gdb) run
Program received signal SIGBUS, Bus error.
0x00000001200005cc in main () at test.c:11
11 *y = 0;
As with most programming problems, there are multiple solutions. In the first solution we simply pad x
with some extra bytes so that y
points to an aligned memory address.
int main(void) {
short x[10+3];
int *y = (int*) (x+4);
*y = 0;
return 0;
}
In the second solution we use types with the same byte alignment to avoid the possibility of an unaligned access.
int main(void) {
int x[10];
int *y = (int*) (x+1);
*y = 0;
return 0;
}
IEEE floating point numbers
Identifying the problem
You will encounter a Floating point exception at runtime.
tcort@topcat ~ $ ./a.out
Floating point exception
tcort@topcat ~ $
Explanation of the problem
When the Alpha architecture's floating point unit was first designed, the designers traded performance for functionality. As a result, all Alpha systems below EV6
do not fully implement the IEEE floating point standard. For those earlier systems, there is no hardware support for denormalized numbers or exceptional IEEE values like not a number and positive/negative infinity. If you wish to use those features, you must enable software assisted IEEE floating point at compile time and have kernel support for software completion enabled.
int main(void) {
/* Expected output: "x/y = inf" */
/* Actual output: "Floating point exception" */
double x = 1.0, y = 0.0;
printf ("x/y = %g\n", x/y);
return 0;
}
Finding a solution
Alphas of model
EV6
or better do not need the -mieee
compiler flag.Enable software assisted floating point at compile time with the -mieee
compiler flag.
tcort@topcat ~ $ gcc -mieee test.c
tcort@topcat ~ $
Using
-mieee
doesn't result in any noticeable slowdown and is recommended in general.Enable kernel floating point software completion
Kernel hacking --->
[*] Kernel debugging
<*> Kernel FP software completion
You can make sure the program is being compiled with the -mieee
compiler flag by checking for _IEEE_FPM
. It is defined when a program is compiled with -mieee
.
#if defined(__alpha) && !defined(_IEEE_FP)
#error "You must compile this program with the -mieee CFLAG on Alpha"
#endif
int main(void) {
double x = 1.0, y = 0.0;
printf ("x/y = %g\n", x/y);
return 0;
}
Forgetting Alpha exists / endianness
Identifying the problem
You will get unexpected and/or incorrect results.
Explanation of the problem
Sometimes programmers don't think about Alpha when conditionally compiling sections of code. They may make assumptions, or only consider a subset of all architectures.
In the following example the programmer assumes that if the system compiling the program is i386
, then it is little endian. The programmer also assumes that the only other system type that would compile this program is ppc
and therefore would be big endian.
int main(void) {
/* Expected output: "3" (works on x86 and ppc) */
/* Actual output: "4" */
int x;
unsigned char y[4];
memset(y,0,sizeof(unsigned char)*4);
y[0]=0xff; y[1]=0x00;
y[2]=0xff; y[4]=0x00;
memcpy(&x,y,4);
#ifdef __i386
/* little endian */
x = (x&1) + 2;
#else /* __ppc */
/* big endian */
x = (x&1) + 3;
#endif
printf("%d\n",x);
return 0;
}
Finding a solution
When determining whether the system compiling your program has a particular property (example, little endian vs. big endian), you should never use __arch
. If you do, then to ensure portability you will have to determine if the property exists or not for every architecture, and you will have to add new architectures as they are created. Instead you should use preprocessor definitions, or a runtime check if the definitions are not available.
#include <sys/param.h>
int is_big_endian() {
long l = 1;
return !(*((char *)(&l)));
}
int main(void) {
int x;
unsigned char y[4];
memset(y,0,sizeof(unsigned char)*4);
y[0]=0xff; y[1]=0x00;
y[2]=0xff; y[4]=0x00;
memcpy(&x,y,4);
#ifdef __BYTE_ORDER
# if __BYTE_ORDER == __LITTLE_ENDIAN
x = (x&1) + 2;
# else
# if __BYTE_ORDER == __BIG_ENDIAN
x = (x&1) + 3;
# else
if (is_big_endian()) {
x = (x&1) + 3;
} else {
x = (x&1) + 2;
}
# endif
# endif
#else
if (is_big_endian()) {
x = (x&1) + 3;
} else {
x = (x&1) + 2;
}
#endif
printf("%d\n",x);
return 0;
}
Variable length parameter lists
Identifying the problem
You will get unexpected and/or incorrect results or possibly a crash.
Explanation of the problem
Functions like printf
can accept any number of arguments. The arguments are stored on the stack. Generally, they are accessed by doing pointer arithmetic and casting. The direction in which you iterate over the remaining arguments varies depending on architecture / operating system. On some systems you increment the pointer to reach the next parameter, on other systems you decrement the pointer to reach the next parameter. Furthermore, there is no compile time storage size check, nor type checking.
int sum(int n, ...) {
int sum, *arg;
/* sum n parameters (excluding the first) */
for(sum=0, arg=(int*)&n+1; n--; sum+=*arg++)
/* nothing */;
return sum;
}
int main(void) {
/* Expected output: "1 + 2 + 3 - 2 = 4" */
/* Actual output: "1 + 2 + 3 - 2 = 536867769" */
printf("1 + 2 + 3 - 2 = %d\n",sum(4,1,2,3,-2));
return 0;
}
Finding a solution
Since accessing parameters is so system dependent, we cannot easily write very portable code without the help of some preprocessor macros. Fortunately, those preprocessor macros are already written for us.
#include <stdarg.h>
int sum(int n, ...) {
int sum = 0;
va_list v;
va_start(v, n);
while(n--)
sum += va_arg(v, int);
va_end(v);
return sum;
}
int main(void) {
printf("1 + 2 + 3 - 2 = %d\n",sum(4,1,2,3,-2));
return 0;
}
Being portable does not imply being safe. Even if you use the macros in
stdarg.h
you still have to be careful about sizes, types, and the number of parameters. Variable length parameter lists should be used only when absolutely needed.Failure to determine CPU frequency
Identifying the problem
A program that displays or uses the CPU frequency for some purpose will fail at runtime.
Explanation of the problem
There are some programs that determine the CPU frequency from /proc/cpuinfo. The format of this file is slightly different on Alpha. Below are example /proc/cpuinfo listings.
processor : 0
vendor_id : AuthenticAMD
cpu family : 15
model : 36
model name : AMD Turion(tm) 64 Mobile Technology ML-32
stepping : 2
cpu MHz : 1800.000
cache size : 512 KB
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge
mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt
lm 3dnowext 3dnow pni lahf_lm
bogomips : 3604.45
TLB size : 1024 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management: ts fid vid ttp tm stc
Above, in the amd64 example, you will notice that the CPU frequency is given on the cpu MHz
line. Below, in the alpha example, you will notice that the CPU frequency is given on the cycle frequency [Hz]
line.
cpu : Alpha
cpu model : EV56
cpu variation : 7
cpu revision : 0
cpu serial number :
system type : Miata
system variation : 0
system revision : 0
system serial number :
cycle frequency [Hz] : 499714426 est.
timer frequency [Hz] : 1024.00
page size [bytes] : 8192
phys. address bits : 40
max. addr. space # : 127
BogoMIPS : 992.88
kernel unaligned acc : 196 (pc=fffffc0000517e44,va=2000001a032)
user unaligned acc : 171263 (pc=20000e6f610,va=1200c6f26)
platform string : Digital Personal WorkStation 500au
cpus detected : 1
conky is an advanced, highly configurable system monitor for X. The program can display your CPU frequency. Below is the /proc/cpuinfo parser that comes with versions of conky prior to 1.4.1. It doesn't work correctly on alpha. It reports a CPU frequency of 0 MHz.
Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the <ORGANIZATION> nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
{
FILE *f;
char frequency[32];
char s[256];
double freq = 0;
if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
return;
f = fopen(CPUFREQ_CURRENT, "r");
if (f) {
/* if there's a cpufreq /sys node, read the current frequency from this node;
* divide by 1000 to get Mhz. */
if (fgets(s, sizeof(s), f)) {
s[strlen(s)-1] = '\0';
freq = strtod(s, NULL);
}
fclose(f);
snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
return;
}
f = fopen("/proc/cpuinfo", "r"); //open the CPU information file
if (!f)
return;
while (fgets(s, sizeof(s), f) != NULL){ //read the file
#if defined(__i386) || defined(__x86_64)
if (strncmp(s, "cpu MHz", 7) == 0) { //and search for the cpu mhz
#else
if (strncmp(s, "clock", 5) == 0) { // this is different on ppc for some reason
#endif
strcpy(frequency, strchr(s, ':') + 2); //copy just the number
frequency[strlen(frequency) - 1] = '\0'; // strip \n
freq = strtod(frequency, NULL);
break;
}
}
fclose(f);
snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
return;
}
Unlike some of the other portability problems that this document discusses, this problem is not language specific. Here is an example of the problem in python.
import re
def get_freq():
r = re.compile('^cpu MHz\s+:\s+(\d+\.\d+)$', re.M)
m = r.search( open('/proc/cpuinfo').read() )
return float(m.group(1))
Finding a Solution
To solve the problem we make an exception for alpha by using the __alpha
preprocessor definition. We also need to divide the value by 1000000 to get the CPU frequency in MHz.
Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the <ORGANIZATION> nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
{
FILE *f;
char frequency[32];
char s[256];
double freq = 0;
if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
return;
f = fopen(CPUFREQ_CURRENT, "r");
if (f) {
/* if there's a cpufreq /sys node, read the current frequency from this node;
* divide by 1000 to get Mhz. */
if (fgets(s, sizeof(s), f)) {
s[strlen(s)-1] = '\0';
freq = strtod(s, NULL);
}
fclose(f);
snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
return;
}
f = fopen("/proc/cpuinfo", "r"); //open the CPU information file
if (!f)
return;
while (fgets(s, sizeof(s), f) != NULL){ //read the file
#if defined(__i386) || defined(__x86_64)
if (strncmp(s, "cpu MHz", 7) == 0) { //and search for the cpu mhz
#else
#if defined(__alpha)
if (strncmp(s, "cycle frequency [Hz]", 20) == 0) { // different on alpha
#else
if (strncmp(s, "clock", 5) == 0) { // this is different on ppc for some reason
#endif // defined(__alpha)
#endif // defined(__i386) || defined(__x86_64)
strcpy(frequency, strchr(s, ':') + 2); //copy just the number
#if defined(__alpha)
frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz
#else
frequency[strlen(frequency) - 1] = '\0'; // strip \n
freq = strtod(frequency, NULL);
#endif
break;
}
}
fclose(f);
snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
return;
}
The python fix is similar, just make an exception for alpha by using os.uname()[-1]
to determine the system architecture.
import re, os
def get_freq_alpha():
r = re.compile('^cycle frequency \[Hz\]\s+:\s+(\d+)\s+est\.$', re.M)
m = r.search( open('/proc/cpuinfo').read() )
return float(int(m.group(1))/1000000.0)
def get_freq():
if os.uname()[-1] == 'alpha':
return get_freq_alpha()
else:
r = re.compile('^cpu MHz\s+:\s+(\d+\.\d+)$', re.M)
m = r.search( open('/proc/cpuinfo').read() )
return float(m.group(1))