Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 1 | /* |
| 2 | * kmp_utility.c -- Utility routines for the OpenMP support library. |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 3 | */ |
| 4 | |
| 5 | |
| 6 | //===----------------------------------------------------------------------===// |
| 7 | // |
| 8 | // The LLVM Compiler Infrastructure |
| 9 | // |
| 10 | // This file is dual licensed under the MIT and the University of Illinois Open |
| 11 | // Source Licenses. See LICENSE.txt for details. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | |
| 16 | #include "kmp.h" |
| 17 | #include "kmp_wrapper_getpid.h" |
| 18 | #include "kmp_str.h" |
| 19 | #include <float.h> |
| 20 | #include "kmp_i18n.h" |
| 21 | |
| 22 | /* ------------------------------------------------------------------------ */ |
| 23 | /* ------------------------------------------------------------------------ */ |
| 24 | |
| 25 | static const char *unknown = "unknown"; |
| 26 | |
| 27 | #if KMP_ARCH_X86 || KMP_ARCH_X86_64 |
| 28 | |
| 29 | /* NOTE: If called before serial_initialize (i.e. from runtime_initialize), then */ |
| 30 | /* the debugging package has not been initialized yet, and only "0" will print */ |
| 31 | /* debugging output since the environment variables have not been read. */ |
| 32 | |
Jonathan Peyton | 2321d57 | 2015-06-08 19:25:25 +0000 | [diff] [blame] | 33 | #ifdef KMP_DEBUG |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 34 | static int trace_level = 5; |
Jonathan Peyton | 2321d57 | 2015-06-08 19:25:25 +0000 | [diff] [blame] | 35 | #endif |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 36 | |
| 37 | /* |
| 38 | * LOG_ID_BITS = ( 1 + floor( log_2( max( log_per_phy - 1, 1 )))) |
| 39 | * APIC_ID = (PHY_ID << LOG_ID_BITS) | LOG_ID |
| 40 | * PHY_ID = APIC_ID >> LOG_ID_BITS |
| 41 | */ |
| 42 | int |
| 43 | __kmp_get_physical_id( int log_per_phy, int apic_id ) |
| 44 | { |
| 45 | int index_lsb, index_msb, temp; |
| 46 | |
| 47 | if (log_per_phy > 1) { |
| 48 | index_lsb = 0; |
| 49 | index_msb = 31; |
| 50 | |
| 51 | temp = log_per_phy; |
| 52 | while ( (temp & 1) == 0 ) { |
| 53 | temp >>= 1; |
| 54 | index_lsb++; |
| 55 | } |
| 56 | |
| 57 | temp = log_per_phy; |
| 58 | while ( (temp & 0x80000000)==0 ) { |
| 59 | temp <<= 1; |
| 60 | index_msb--; |
| 61 | } |
| 62 | |
| 63 | /* If >1 bits were set in log_per_phy, choose next higher power of 2 */ |
| 64 | if (index_lsb != index_msb) index_msb++; |
| 65 | |
| 66 | return ( (int) (apic_id >> index_msb) ); |
| 67 | } |
| 68 | |
| 69 | return apic_id; |
| 70 | } |
| 71 | |
| 72 | |
| 73 | /* |
| 74 | * LOG_ID_BITS = ( 1 + floor( log_2( max( log_per_phy - 1, 1 )))) |
| 75 | * APIC_ID = (PHY_ID << LOG_ID_BITS) | LOG_ID |
| 76 | * LOG_ID = APIC_ID & (( 1 << LOG_ID_BITS ) - 1 ) |
| 77 | */ |
| 78 | int |
| 79 | __kmp_get_logical_id( int log_per_phy, int apic_id ) |
| 80 | { |
| 81 | unsigned current_bit; |
| 82 | int bits_seen; |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 83 | |
| 84 | if (log_per_phy <= 1) return ( 0 ); |
| 85 | |
| 86 | bits_seen = 0; |
| 87 | |
| 88 | for (current_bit = 1; log_per_phy != 0; current_bit <<= 1) { |
| 89 | if ( log_per_phy & current_bit ) { |
| 90 | log_per_phy &= ~current_bit; |
| 91 | bits_seen++; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /* If exactly 1 bit was set in log_per_phy, choose next lower power of 2 */ |
| 96 | if (bits_seen == 1) { |
| 97 | current_bit >>= 1; |
| 98 | } |
| 99 | |
| 100 | return ( (int) ((current_bit - 1) & apic_id) ); |
| 101 | } |
| 102 | |
| 103 | |
| 104 | static |
| 105 | kmp_uint64 |
| 106 | __kmp_parse_frequency( // R: Frequency in Hz. |
| 107 | char const * frequency // I: Float number and unit: MHz, GHz, or TGz. |
| 108 | ) { |
| 109 | |
| 110 | double value = 0.0; |
| 111 | char const * unit = NULL; |
| 112 | kmp_uint64 result = ~ 0; |
| 113 | |
| 114 | if ( frequency == NULL ) { |
| 115 | return result; |
| 116 | }; // if |
| 117 | value = strtod( frequency, (char * *) & unit ); // strtod() does not like "char conts *". |
| 118 | if ( 0 < value && value <= DBL_MAX ) { // Good value (not overflow, underflow, etc). |
| 119 | if ( strcmp( unit, "MHz" ) == 0 ) { |
| 120 | value = value * 1.0E+6; |
| 121 | } else if ( strcmp( unit, "GHz" ) == 0 ) { |
| 122 | value = value * 1.0E+9; |
| 123 | } else if ( strcmp( unit, "THz" ) == 0 ) { |
| 124 | value = value * 1.0E+12; |
| 125 | } else { // Wrong unit. |
| 126 | return result; |
| 127 | }; // if |
| 128 | result = value; |
| 129 | }; // if |
| 130 | return result; |
| 131 | |
| 132 | }; // func __kmp_parse_cpu_frequency |
| 133 | |
| 134 | void |
| 135 | __kmp_query_cpuid( kmp_cpuinfo_t *p ) |
| 136 | { |
| 137 | struct kmp_cpuid buf; |
| 138 | int max_arg; |
| 139 | int log_per_phy; |
Jonathan Peyton | 2321d57 | 2015-06-08 19:25:25 +0000 | [diff] [blame] | 140 | #ifdef KMP_DEBUG |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 141 | int cflush_size; |
Jonathan Peyton | 2321d57 | 2015-06-08 19:25:25 +0000 | [diff] [blame] | 142 | #endif |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 143 | |
| 144 | p->initialized = 1; |
| 145 | |
| 146 | p->sse2 = 1; // Assume SSE2 by default. |
| 147 | |
| 148 | __kmp_x86_cpuid( 0, 0, &buf ); |
| 149 | |
| 150 | KA_TRACE( trace_level, ("INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n", |
| 151 | 0, buf.eax, buf.ebx, buf.ecx, buf.edx ) ); |
| 152 | |
| 153 | max_arg = buf.eax; |
| 154 | |
| 155 | p->apic_id = -1; |
| 156 | |
| 157 | if (max_arg >= 1) { |
| 158 | int i; |
| 159 | kmp_uint32 t, data[ 4 ]; |
| 160 | |
| 161 | __kmp_x86_cpuid( 1, 0, &buf ); |
| 162 | KA_TRACE( trace_level, ("INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n", |
| 163 | 1, buf.eax, buf.ebx, buf.ecx, buf.edx ) ); |
| 164 | |
| 165 | { |
| 166 | #define get_value(reg,lo,mask) ( ( ( reg ) >> ( lo ) ) & ( mask ) ) |
| 167 | |
| 168 | p->signature = buf.eax; |
| 169 | p->family = get_value( buf.eax, 20, 0xff ) + get_value( buf.eax, 8, 0x0f ); |
| 170 | p->model = ( get_value( buf.eax, 16, 0x0f ) << 4 ) + get_value( buf.eax, 4, 0x0f ); |
| 171 | p->stepping = get_value( buf.eax, 0, 0x0f ); |
| 172 | |
| 173 | #undef get_value |
| 174 | |
| 175 | KA_TRACE( trace_level, (" family = %d, model = %d, stepping = %d\n", p->family, p->model, p->stepping ) ); |
| 176 | } |
| 177 | |
| 178 | for ( t = buf.ebx, i = 0; i < 4; t >>= 8, ++i ) { |
| 179 | data[ i ] = (t & 0xff); |
| 180 | }; // for |
| 181 | |
| 182 | p->sse2 = ( buf.edx >> 26 ) & 1; |
| 183 | |
| 184 | #ifdef KMP_DEBUG |
| 185 | |
| 186 | if ( (buf.edx >> 4) & 1 ) { |
| 187 | /* TSC - Timestamp Counter Available */ |
| 188 | KA_TRACE( trace_level, (" TSC" ) ); |
| 189 | } |
| 190 | if ( (buf.edx >> 8) & 1 ) { |
| 191 | /* CX8 - CMPXCHG8B Instruction Available */ |
| 192 | KA_TRACE( trace_level, (" CX8" ) ); |
| 193 | } |
| 194 | if ( (buf.edx >> 9) & 1 ) { |
| 195 | /* APIC - Local APIC Present (multi-processor operation support */ |
| 196 | KA_TRACE( trace_level, (" APIC" ) ); |
| 197 | } |
| 198 | if ( (buf.edx >> 15) & 1 ) { |
| 199 | /* CMOV - Conditional MOVe Instruction Available */ |
| 200 | KA_TRACE( trace_level, (" CMOV" ) ); |
| 201 | } |
| 202 | if ( (buf.edx >> 18) & 1 ) { |
| 203 | /* PSN - Processor Serial Number Available */ |
| 204 | KA_TRACE( trace_level, (" PSN" ) ); |
| 205 | } |
| 206 | if ( (buf.edx >> 19) & 1 ) { |
| 207 | /* CLFULSH - Cache Flush Instruction Available */ |
| 208 | cflush_size = data[ 1 ] * 8; /* Bits 15-08: CLFLUSH line size = 8 (64 bytes) */ |
| 209 | KA_TRACE( trace_level, (" CLFLUSH(%db)", cflush_size ) ); |
| 210 | |
| 211 | } |
| 212 | if ( (buf.edx >> 21) & 1 ) { |
| 213 | /* DTES - Debug Trace & EMON Store */ |
| 214 | KA_TRACE( trace_level, (" DTES" ) ); |
| 215 | } |
| 216 | if ( (buf.edx >> 22) & 1 ) { |
| 217 | /* ACPI - ACPI Support Available */ |
| 218 | KA_TRACE( trace_level, (" ACPI" ) ); |
| 219 | } |
| 220 | if ( (buf.edx >> 23) & 1 ) { |
| 221 | /* MMX - Multimedia Extensions */ |
| 222 | KA_TRACE( trace_level, (" MMX" ) ); |
| 223 | } |
| 224 | if ( (buf.edx >> 25) & 1 ) { |
| 225 | /* SSE - SSE Instructions */ |
| 226 | KA_TRACE( trace_level, (" SSE" ) ); |
| 227 | } |
| 228 | if ( (buf.edx >> 26) & 1 ) { |
| 229 | /* SSE2 - SSE2 Instructions */ |
| 230 | KA_TRACE( trace_level, (" SSE2" ) ); |
| 231 | } |
| 232 | if ( (buf.edx >> 27) & 1 ) { |
| 233 | /* SLFSNP - Self-Snooping Cache */ |
| 234 | KA_TRACE( trace_level, (" SLFSNP" ) ); |
| 235 | } |
| 236 | #endif /* KMP_DEBUG */ |
| 237 | |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 238 | if ( (buf.edx >> 28) & 1 ) { |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 239 | /* Bits 23-16: Logical Processors per Physical Processor (1 for P4) */ |
| 240 | log_per_phy = data[ 2 ]; |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 241 | p->apic_id = data[ 3 ]; /* Bits 31-24: Processor Initial APIC ID (X) */ |
| 242 | KA_TRACE( trace_level, (" HT(%d TPUs)", log_per_phy ) ); |
| 243 | |
| 244 | if( log_per_phy > 1 ) { |
| 245 | /* default to 1k FOR JT-enabled processors (4k on OS X*) */ |
| 246 | #if KMP_OS_DARWIN |
| 247 | p->cpu_stackoffset = 4 * 1024; |
| 248 | #else |
| 249 | p->cpu_stackoffset = 1 * 1024; |
| 250 | #endif |
| 251 | } |
| 252 | |
| 253 | p->physical_id = __kmp_get_physical_id( log_per_phy, p->apic_id ); |
| 254 | p->logical_id = __kmp_get_logical_id( log_per_phy, p->apic_id ); |
| 255 | } |
| 256 | #ifdef KMP_DEBUG |
| 257 | if ( (buf.edx >> 29) & 1 ) { |
| 258 | /* ATHROTL - Automatic Throttle Control */ |
| 259 | KA_TRACE( trace_level, (" ATHROTL" ) ); |
| 260 | } |
| 261 | KA_TRACE( trace_level, (" ]\n" ) ); |
| 262 | |
| 263 | for (i = 2; i <= max_arg; ++i) { |
| 264 | __kmp_x86_cpuid( i, 0, &buf ); |
| 265 | KA_TRACE( trace_level, |
| 266 | ( "INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n", |
| 267 | i, buf.eax, buf.ebx, buf.ecx, buf.edx ) ); |
| 268 | } |
| 269 | #endif |
| 270 | #if KMP_USE_ADAPTIVE_LOCKS |
| 271 | p->rtm = 0; |
| 272 | if (max_arg > 7) |
| 273 | { |
| 274 | /* RTM bit CPUID.07:EBX, bit 11 */ |
| 275 | __kmp_x86_cpuid(7, 0, &buf); |
| 276 | p->rtm = (buf.ebx >> 11) & 1; |
| 277 | KA_TRACE( trace_level, (" RTM" ) ); |
| 278 | } |
| 279 | #endif |
| 280 | }; // if |
| 281 | |
| 282 | { // Parse CPU brand string for frequency. |
| 283 | |
| 284 | union kmp_cpu_brand_string { |
| 285 | struct kmp_cpuid buf[ 3 ]; |
| 286 | char string[ sizeof( struct kmp_cpuid ) * 3 + 1 ]; |
| 287 | }; // union kmp_cpu_brand_string |
| 288 | union kmp_cpu_brand_string brand; |
| 289 | int i; |
| 290 | |
| 291 | p->frequency = 0; |
| 292 | |
| 293 | // Get CPU brand string. |
| 294 | for ( i = 0; i < 3; ++ i ) { |
| 295 | __kmp_x86_cpuid( 0x80000002 + i, 0, &brand.buf[ i ] ); |
| 296 | }; // for |
| 297 | brand.string[ sizeof( brand.string ) - 1 ] = 0; // Just in case. ;-) |
| 298 | KA_TRACE( trace_level, ( "cpu brand string: \"%s\"\n", brand.string ) ); |
| 299 | |
| 300 | // Parse frequency. |
| 301 | p->frequency = __kmp_parse_frequency( strrchr( brand.string, ' ' ) ); |
| 302 | KA_TRACE( trace_level, ( "cpu frequency from brand string: %" KMP_UINT64_SPEC "\n", p->frequency ) ); |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | #endif /* KMP_ARCH_X86 || KMP_ARCH_X86_64 */ |
| 307 | |
| 308 | /* ------------------------------------------------------------------------------------ */ |
| 309 | /* ------------------------------------------------------------------------------------ */ |
| 310 | |
| 311 | void |
| 312 | __kmp_expand_host_name( char *buffer, size_t size ) |
| 313 | { |
| 314 | KMP_DEBUG_ASSERT(size >= sizeof(unknown)); |
| 315 | #if KMP_OS_WINDOWS |
| 316 | { |
| 317 | DWORD s = size; |
| 318 | |
| 319 | if (! GetComputerNameA( buffer, & s )) |
Andrey Churbanov | 74bf17b | 2015-04-02 13:27:08 +0000 | [diff] [blame] | 320 | KMP_STRCPY_S( buffer, size, unknown ); |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 321 | } |
| 322 | #else |
| 323 | buffer[size - 2] = 0; |
| 324 | if (gethostname( buffer, size ) || buffer[size - 2] != 0) |
Andrey Churbanov | 74bf17b | 2015-04-02 13:27:08 +0000 | [diff] [blame] | 325 | KMP_STRCPY_S( buffer, size, unknown ); |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 326 | #endif |
| 327 | } |
| 328 | |
| 329 | /* Expand the meta characters in the filename: |
| 330 | * |
| 331 | * Currently defined characters are: |
| 332 | * |
| 333 | * %H the hostname |
| 334 | * %P the number of threads used. |
| 335 | * %I the unique identifier for this run. |
| 336 | */ |
| 337 | |
| 338 | void |
| 339 | __kmp_expand_file_name( char *result, size_t rlen, char *pattern ) |
| 340 | { |
| 341 | char *pos = result, *end = result + rlen - 1; |
| 342 | char buffer[256]; |
| 343 | int default_cpu_width = 1; |
| 344 | int snp_result; |
| 345 | |
| 346 | KMP_DEBUG_ASSERT(rlen > 0); |
| 347 | *end = 0; |
| 348 | { |
| 349 | int i; |
| 350 | for(i = __kmp_xproc; i >= 10; i /= 10, ++default_cpu_width); |
| 351 | } |
| 352 | |
| 353 | if (pattern != NULL) { |
| 354 | while (*pattern != '\0' && pos < end) { |
| 355 | if (*pattern != '%') { |
| 356 | *pos++ = *pattern++; |
| 357 | } else { |
| 358 | char *old_pattern = pattern; |
| 359 | int width = 1; |
| 360 | int cpu_width = default_cpu_width; |
| 361 | |
| 362 | ++pattern; |
| 363 | |
| 364 | if (*pattern >= '0' && *pattern <= '9') { |
| 365 | width = 0; |
| 366 | do { |
| 367 | width = (width * 10) + *pattern++ - '0'; |
| 368 | } while (*pattern >= '0' && *pattern <= '9'); |
| 369 | if (width < 0 || width > 1024) |
| 370 | width = 1; |
| 371 | |
| 372 | cpu_width = width; |
| 373 | } |
| 374 | |
| 375 | switch (*pattern) { |
| 376 | case 'H': |
| 377 | case 'h': |
| 378 | { |
| 379 | __kmp_expand_host_name( buffer, sizeof( buffer ) ); |
Andrey Churbanov | 74bf17b | 2015-04-02 13:27:08 +0000 | [diff] [blame] | 380 | KMP_STRNCPY( pos, buffer, end - pos + 1); |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 381 | if(*end == 0) { |
| 382 | while ( *pos ) |
| 383 | ++pos; |
| 384 | ++pattern; |
| 385 | } else |
| 386 | pos = end; |
| 387 | } |
| 388 | break; |
| 389 | case 'P': |
| 390 | case 'p': |
| 391 | { |
Andrey Churbanov | 74bf17b | 2015-04-02 13:27:08 +0000 | [diff] [blame] | 392 | snp_result = KMP_SNPRINTF( pos, end - pos + 1, "%0*d", cpu_width, __kmp_dflt_team_nth ); |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 393 | if(snp_result >= 0 && snp_result <= end - pos) { |
| 394 | while ( *pos ) |
| 395 | ++pos; |
| 396 | ++pattern; |
| 397 | } else |
| 398 | pos = end; |
| 399 | } |
| 400 | break; |
| 401 | case 'I': |
| 402 | case 'i': |
| 403 | { |
| 404 | pid_t id = getpid(); |
Andrey Churbanov | 74bf17b | 2015-04-02 13:27:08 +0000 | [diff] [blame] | 405 | snp_result = KMP_SNPRINTF( pos, end - pos + 1, "%0*d", width, id ); |
Jim Cownie | 5e8470a | 2013-09-27 10:38:44 +0000 | [diff] [blame] | 406 | if(snp_result >= 0 && snp_result <= end - pos) { |
| 407 | while ( *pos ) |
| 408 | ++pos; |
| 409 | ++pattern; |
| 410 | } else |
| 411 | pos = end; |
| 412 | break; |
| 413 | } |
| 414 | case '%': |
| 415 | { |
| 416 | *pos++ = '%'; |
| 417 | ++pattern; |
| 418 | break; |
| 419 | } |
| 420 | default: |
| 421 | { |
| 422 | *pos++ = '%'; |
| 423 | pattern = old_pattern + 1; |
| 424 | break; |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | /* TODO: How do we get rid of this? */ |
| 430 | if(*pattern != '\0') |
| 431 | KMP_FATAL( FileNameTooLong ); |
| 432 | } |
| 433 | |
| 434 | *pos = '\0'; |
| 435 | } |
| 436 | |