#/* -*- Mode: C -*- */ #/* Author: Wilson Snyder */ #/* IRIX & FreeBSD port by: Daniel Gustafson */ #/*##################################################################### */ #/* */ #/* Copyright 1999-2009 by Wilson Snyder. This program is free software; */ #/* you can redistribute it and/or modify it under the terms of either the GNU */ #/* Lesser General Public License Version 3 or the Perl Artistic License Version 2.0. */ #/* */ #/* This program is distributed in the hope that it will be useful, */ #/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ #/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ #/* GNU General Public License for more details. */ #/* */ #/*##################################################################### */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include #include #include #include #if defined(_AIX) # define AIX #endif #if defined(hpux) || defined(__hpux) # define HPUX #endif #if defined(__osf__) && (defined(__alpha) || defined(__alpha__)) # define OSF_ALPHA #endif #if defined(__mips) # define MIPS # if defined(sgi) # define IRIX # endif #endif #if defined(sun) || defined(__sun__) # define SUNOS #endif #ifdef AIX # ifdef HAS_PMAPI # include # endif # ifdef HAS_PERFSTAT # include # endif #endif #ifdef HPUX #include #include struct pst_dynamic psd; #endif #ifdef OSF_ALPHA #include #include #endif #if defined(MIPS) && !defined(__linux__) #include #endif #ifdef IRIX #include #include #include #include #include #include #include #include #endif #ifdef SUNOS #include #endif #if defined(__FreeBSD__) || defined(__APPLE__) #include #include #include #include #endif /* Missing in older headers */ #ifndef P_POWEROFF #define P_POWEROFF 5 #endif typedef int CpuNumFromRef_t; #/**************************************************************/ #ifdef __linux__ const char* proc_cpuinfo_field (const char *field) /* Return string from a field of /proc/cpuinfo, NULL if not found */ /* Comparison is case insensitive */ /* If multiple matches, the last match is returned */ { FILE* fp; static char line[1000]; static char result[1000]; int len = strlen(field); char* resultp = NULL; if (NULL!=(fp = fopen ("/proc/cpuinfo", "r"))) { while (!feof(fp)) { const char* ok = fgets (line, 990, fp); if (ok && 0==strncasecmp (field, line, len)) { char *loc = strchr (line, ':'); if (loc) { strncpy(result,loc+2, 990); resultp = result; loc = strchr (result, '\n'); if (loc) *loc = '\0'; /*printf("MATCH: %s: %s\n", field, resultp);*/ } } } fclose(fp); } return (resultp); } int _proc_cpuinfo_clock_calc (void) { const char* value; value = proc_cpuinfo_field ("cpu MHz"); if (value) return (atoi(value)); value = proc_cpuinfo_field ("clock"); if (value) return (atoi(value)); value = proc_cpuinfo_field ("bogomips"); if (value) return (atoi(value)); return (0); } int proc_cpuinfo_clock (void) /* Return clock frequency */ { static int cache = 0; if (!cache) cache = _proc_cpuinfo_clock_calc(); return cache; } #endif int _proc_nthreaders_calc (void) { int num_cpus = 0; /* Determine how many processors are online and available */ #ifdef HPUX if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1) num_cpus = psd.psd_proc_cnt; #endif #ifdef OSF_ALPHA getsysinfo(GSI_CPUS_IN_BOX,&num_cpus,sizeof(num_cpus),0,0) #endif #if defined(MIPS) && !defined(__linux__) char buf[16]; if (sysinfo(_MIPS_SI_NUM_PROCESSORS, buf, 10) != -1) num_cpus = atoi(buf); #endif /* Generic linux defaults */ #if defined(SUNOS) || defined(AIX) || defined (__linux__) if (num_cpus < 1) num_cpus = sysconf(_SC_NPROCESSORS_ONLN); # ifdef __linux__ if (num_cpus < 1) { /* SPARC Linux has a bug where SC_NPROCESSORS is set to 0. */ const char* value = proc_cpuinfo_field("ncpus active"); if (value) num_cpus = atoi(value); } # endif #endif #if defined(__FreeBSD__) || defined(HW_NCPU) int len = sizeof(num_cpus); sysctlbyname("hw.ncpu", &num_cpus, (void*)(&len), NULL, 0); #endif if (num_cpus < 1) num_cpus=1; /* We're running this program, after all :-) */ return (num_cpus); } int proc_nthreaders (void) /* Return number of processor threads */ { static int cache = 0; if (!cache) cache = _proc_nthreaders_calc(); return cache; } #ifdef IRIX /* invent_cpuinfo_t irix_get_cpuinf(int cpuid); * Returns an invent_cpuinfo_t regarding the requested cpuid. */ invent_cpuinfo_t irix_get_cpuinf(int cpuid) { union { invent_generic_t generic; invent_cpuinfo_t cpu; } hw_inv; int attr_len = sizeof(hw_inv); DIR *hw_graph; struct dirent *hw_entry; char *hw_entry_buf = (char *)malloc(MAXPATHLEN); char *hw_filename = (char *)malloc(MAXPATHLEN); if ((hw_graph = opendir("/hw/cpunum")) != NULL) { while ((hw_entry = readdir(hw_graph)) != NULL) { if ((strcmp(hw_entry->d_name, ".") != 0) && (strcmp(hw_entry->d_name, "..") != 0)) { strcpy(hw_filename, "/hw/cpunum/"); strncat(hw_filename, hw_entry->d_name, 1); if (realpath(hw_filename, hw_entry_buf) != NULL) { if (attr_get(hw_entry_buf, INFO_LBL_DETAIL_INVENT, (char *)&hw_inv, &attr_len, 0) == 0) { if (hw_inv.generic.ig_invclass == INV_PROCESSOR) { if (hw_inv.cpu.ic_cpuid == cpuid) { break; } } } } } } } closedir(hw_graph); return(hw_inv.cpu); } #endif int logical_per_physical_cpu() { int logical_per = 1; #ifdef __linux__ const char* flags = proc_cpuinfo_field ("flags"); /* flags: ... ht ... indicates hyperthreading enabled on a cpu */ if (flags && strstr (flags, " ht ")) { /* HACK: Current linux under hyperthreading always makes 2 logical CPUs per physical CPU */ logical_per = 2; } #endif #ifdef __FreeBSD__ int hlt_htt_cpu = 0; int len = sizeof(hlt_htt_cpu); if (sysctlbyname("machdep.hlt_logical_cpus", &hlt_htt_cpu, &len, NULL, 0) == 0) { if (hlt_htt_cpu == 0) { /* HACK: Current FreeBSD under hyperthreading always makes 2 logical CPUs per physical CPU */ logical_per = 2; } } #endif return logical_per; } int proc_num_physical_ids() { #ifdef __linux__ { const char* value = proc_cpuinfo_field ("physical id"); if (value) { int phys = atoi(value)+1; return phys; } } #endif return 0; } int _proc_ncores_calc() { /* Can't have more cores than threaders */ int num = proc_nthreaders(); #ifdef __linux__ { int phys = proc_num_physical_ids(); const char* cvalue = proc_cpuinfo_field ("cpu cores"); if (phys && cvalue) { int cores_per_phys = atoi(cvalue); int cores = phys * cores_per_phys; if (cores && (cores <= num)) return cores; } } #endif { /* If hyperthreading, the threader count is too high */ if (num > 1) { num /= logical_per_physical_cpu(); } return num; } } int proc_ncores() { /* Return number of processor cores */ static int cache = 0; if (!cache) cache = _proc_ncores_calc(); return cache; } int _proc_nsockets_calc() { int num = proc_ncores(); /* default to core count, trim downwards if we can */ int phys = proc_num_physical_ids(); if (phys && ((phys <= num))) num = phys; return num; } int proc_nsockets() { /* Return number of processor sockets */ static int cache = 0; if (!cache) cache = _proc_nsockets_calc(); return cache; } MODULE = Unix::Processors PACKAGE = Unix::Processors #/**********************************************************************/ #/* class->max_online() */ #/* Self is a argument, but we don't need it */ #/* We use sysconf, as that is more portable */ #/* Other packages also provide sysconf, but saves downloading them... */ long max_online(self) SV *self; CODE: { if (self) {} /* Prevent unused warning */ RETVAL = proc_nthreaders(); } OUTPUT: RETVAL #/**********************************************************************/ #/* class->max_physical() */ #/* Self is a argument, but we don't need it */ long max_physical(self) SV *self; CODE: { if (self) {} /* Prevent unused warning */ RETVAL = proc_ncores(); } OUTPUT: RETVAL #/**********************************************************************/ #/* class->max_socket() */ #/* Self is a argument, but we don't need it */ long max_socket(self) SV *self; CODE: { if (self) {} /* Prevent unused warning */ RETVAL = proc_nsockets(); } OUTPUT: RETVAL #/**********************************************************************/ #/* class->max_clock() */ #/* Self is a argument, but we don't need it */ int max_clock(self) SV *self; CODE: { int clock = 0; #ifdef AIX # if defined(HAS_PERFSTAT) perfstat_cpu_total_t data; if (perfstat_cpu_total (0, &data, sizeof(data), 1)) { clock = data.processorHZ / 1000000; } # elif defined(HAS_PMAPI) /* pm_cycles uses an approximation to arrive at cycle time * so we round up to the nearest Mhz */ clock = (int)((pm_cycles() + 500000) / 1000000); # endif #endif #ifdef HPUX /* all processors have the same clock on HP - just report the first one */ struct pst_processor psp; if (pstat_getprocessor(&psp, sizeof(psp), 1, 0)) { clock = psp.psp_iticksperclktick / 10000; } #endif #ifdef SUNOS int cpu; int last_cpu = 0; processor_info_t info, *infop=&info; for (cpu=0; cpu < last_cpu+16; cpu++) { if (processor_info (cpu, infop)==0 && infop->pi_state == P_ONLINE) { if (clock < infop->pi_clock) { clock = infop->pi_clock; } last_cpu = cpu; } } #endif #ifdef IRIX int num_cpus = proc_nthreaders(); if ((num_cpus > 0) && (num_cpus < 3)) { inventory_t *sys_invent; if (setinvent() != -1) { for (sys_invent = getinvent(); (sys_invent); sys_invent = getinvent()) { if ((sys_invent->inv_class == INV_PROCESSOR) && (sys_invent->inv_type == INV_CPUBOARD)) { clock = sys_invent->inv_controller; break; } } endinvent(); } } else { invent_cpuinfo_t cpu_info; int i; for (i = 0; i < proc_nthreaders(); i++) { cpu_info = irix_get_cpuinf(i); if (cpu_info.ic_cpuid == i) if (cpu_info.ic_cpu_info.cpufq > clock) clock = cpu_info.ic_cpu_info.cpufq; } } #endif #if (defined(__FreeBSD__) && (__FreeBSD_version >= 503105)) int value = 0; int len = sizeof(value); /* * Even if the frequency is modified using cpu_freq(3), all cpus * have the same value why we can request CPU 0 for max_clock. */ if (sysctlbyname("dev.cpu.0.freq", &value, &len, NULL, 0) == 0) { clock = value; } #elif defined(HW_CPU_FREQ) long long value = 0; int len = sizeof(value); if (sysctlbyname("hw.cpufrequency", &value, (void*)(&len), NULL, 0) == 0) { clock = value; } #endif #ifdef __linux__ int value = proc_cpuinfo_clock(); if (value) clock = value; #endif if (self) {} /* Prevent unused warning */ RETVAL = clock; } OUTPUT: RETVAL #/**********************************************************************/ #/**********************************************************************/ #/**********************************************************************/ #/**********************************************************************/ #/**********************************************************************/ MODULE = Unix::Processors PACKAGE = Unix::Processors::Info #/**********************************************************************/ #/* class->id() */ int id (cpu) CpuNumFromRef_t cpu PROTOTYPE: $ CODE: { RETVAL = cpu; } OUTPUT: RETVAL #/**********************************************************************/ #/* class->clock() */ SV * clock (cpu) CpuNumFromRef_t cpu PROTOTYPE: $ CODE: { int value = 0; #ifdef AIX int num_cpus = proc_nthreaders(); if (cpu < num_cpus) { # if defined(HAS_PERFSTAT) perfstat_cpu_total_t data; if (perfstat_cpu_total (0, &data, sizeof(data), 1)) { value = data.processorHZ / 1000000; } # elif defined(HAS_PMAPI) /* pm_cycles uses an approximation to arrive at cycle time * so we round up to the nearest Mhz */ clock = (int)((pm_cycles() + 500000) / 1000000); # endif } #endif #ifdef HPUX int num_cpus = proc_nthreaders(); if (cpu < num_cpus) { /* all processors have the same clock on HP - just report the first one */ struct pst_processor psp; if (pstat_getprocessor(&psp, sizeof(psp), 1, 0)) { value = psp.psp_iticksperclktick / 10000; } } #endif #ifdef SUNOS processor_info_t info, *infop=&info; if (processor_info (cpu, infop)==0) { value = infop->pi_clock; } #endif #ifdef IRIX int num_cpus = proc_nthreaders(); if ((num_cpus > 0) && (num_cpus < 3)) { inventory_t *sys_invent; if (setinvent() != -1) { while ((sys_invent = getinvent()) != NULL) { if ((sys_invent->inv_class == INV_PROCESSOR) && (sys_invent->inv_type == INV_CPUBOARD)) { value = sys_invent->inv_controller; break; } } endinvent(); } } else { invent_cpuinfo_t cpu_info; cpu_info = irix_get_cpuinf(cpu); if (cpu_info.ic_cpuid == cpu) value = cpu_info.ic_cpu_info.cpufq; } #endif #if (defined(__FreeBSD__) && (__FreeBSD_version >= 503105)) int cpu_freq = 0; int len = sizeof(cpu_freq); char cpu_freq_req[16]; snprintf(cpu_freq_req, 16, "dev.cpu.%d.freq", cpu); if (sysctlbyname(cpu_freq_req, &cpu_freq, &len, NULL, 0) == 0) { value = cpu_freq; } #endif #ifdef __linux__ /* Cheat... Same clock for every CPU */ value = proc_cpuinfo_clock(); #endif if (value) { ST(0) = sv_newmortal(); sv_setiv (ST(0), value); } else { ST(0) = &PL_sv_undef; } } #/**********************************************************************/ #/* class->state() */ SV * state (cpu) CpuNumFromRef_t cpu PROTOTYPE: $ CODE: { char *value = NULL; #ifdef SUNOS processor_info_t info, *infop=&info; if (processor_info (cpu, infop)==0) { switch (infop->pi_state) { case P_ONLINE: value = "online"; break; case P_OFFLINE: value = "offline"; break; case P_POWEROFF: value = "poweroff"; break; } } #endif #ifdef IRIX int num_cpus; if ((num_cpus = sysmp(MP_NPROCS)) != -1) { struct pda_stat proc_info[num_cpus]; if (sysmp(MP_STAT, proc_info) != -1) { if (proc_info[cpu].p_flags == PDAF_MASTER) value = "MASTER"; else if (proc_info[cpu].p_flags == PDAF_CLOCK) value = "CLOCK"; else if (proc_info[cpu].p_flags == PDAF_ENABLED) value = "ENABLED"; else if (proc_info[cpu].p_flags == PDAF_FASTCLOCK) value = "FASTCLOCK"; else if (proc_info[cpu].p_flags == PDAF_ISOLATED) value = "ISOLATED"; else if (proc_info[cpu].p_flags == PDAF_BROADCAST_OFF) value = "BROADCAST_OFF"; else if (proc_info[cpu].p_flags == PDAF_NONPREEMPTIVE) value = "NONPREEMPTIVE"; else if (proc_info[cpu].p_flags == PDAF_NOINTR) value = "NOINTR"; else if (proc_info[cpu].p_flags == PDAF_ITHREADSOK) value = "ITHREADSOK"; else if (proc_info[cpu].p_flags == PDAF_DISABLE_CPU) value = "DISABLE_CPU"; else if (proc_info[cpu].p_flags == PDAF_EXCLUDED) value = "EXCLUDED"; else /* * No p_flags value is specified for uniprocessor * systems. Return ONLINE. */ value = "ONLINE"; } } #endif /* Cheat... Assume all online */ if (value == NULL) value = "online"; /* Return it */ if (value) { ST(0) = sv_newmortal(); sv_setpv (ST(0), value); } else { ST(0) = &PL_sv_undef; } } #/**********************************************************************/ #/* class->type() */ SV * type (cpu) CpuNumFromRef_t cpu PROTOTYPE: $ CODE: { const char* value = NULL; #ifdef AIX # if defined(HAS_PERFSTAT) int num_cpus = proc_nthreaders(); if (cpu < num_cpus) { perfstat_cpu_total_t data; if (perfstat_cpu_total (0, &data, sizeof(data), 1)) { value = data.description; } } # endif #endif #ifdef HPUX int num_cpus = proc_nthreaders(); if (cpu < num_cpus) { switch(sysconf(_SC_CPU_VERSION)) { case CPU_PA_RISC1_0: value = "HP PA-RISC 1.0"; break; case CPU_PA_RISC1_1: value = "HP PA-RISC 1.1"; break; case CPU_PA_RISC1_2: value = "HP PA-RISC 1.2"; break; case CPU_PA_RISC2_0: value = "HP PA-RISC 2.0"; break; } } #endif #ifdef SUNOS processor_info_t info, *infop=&info; if (processor_info (cpu, infop)==0) { value = infop->pi_processor_type; } #endif #ifdef __linux__ int ncpu = proc_nthreaders(); if (cpu < ncpu) { value = proc_cpuinfo_field ("model name"); if (!value) value = proc_cpuinfo_field ("machine"); if (!value) value = proc_cpuinfo_field ("family"); } #endif #if defined(MIPS) && !defined(__linux__) if (cpu < proc_nthreaders()) { if ((value = (char *)malloc(64)) != NULL) { sysinfo(SI_MACHINE, value, 64); } } #endif #ifdef IRIX if (cpu < proc_nthreaders()) { int cpu_data = 0; int num_cpus = proc_nthreaders(); if ((num_cpus > 0) && (num_cpus < 3)) { inventory_t *sys_invent; if (setinvent() != -1) { while ((sys_invent = getinvent()) != NULL) { if ((sys_invent->inv_class == INV_PROCESSOR) && (sys_invent->inv_type == INV_CPUCHIP)) { cpu_data = sys_invent->inv_state; break; } } endinvent(); } } else { invent_cpuinfo_t cpu_info; cpu_info = irix_get_cpuinf(cpu); if (cpu_info.ic_cpuid == cpu) cpu_data = cpu_info.ic_cpu_info.cpuflavor; } if (cpu_data != 0) { if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R2000A) strcat(value, " MIPS R2000A"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R2000) strcat(value, " MIPS R2000A"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R3000A) strcat(value, " MIPS R3000A"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R3000) strcat(value, " MIPS R3000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R4000) { if (((cpu_data&C0_MAJREVMASK)>>C0_MAJREVSHIFT) >= C0_MAJREVMIN_R4400) strcat(value, " MIPS R4400"); else strcat(value, " MIPS R4000"); } else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R4650) strcat(value, " MIPS R4650"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R4700) strcat(value, " MIPS R4700"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R4600) strcat(value, " MIPS R4600"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R5000) strcat(value, " MIPS R5000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_RM5271) strcat(value, " MIPS RM5271"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R6000A) strcat(value, " MIPS R6000A"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R6000) strcat(value, " MIPS R6000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_RM7000) strcat(value, " MIPS RM7000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R8000) strcat(value, " MIPS R8000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R10000) strcat(value, " MIPS R10000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R12000) strcat(value, " MIPS R12000"); else if ((cpu_data >> C0_IMPSHIFT) == C0_IMP_R14000) strcat(value, " MIPS R14000"); else strcat(value, " Undefined MIPS"); sprintf(value, "%s Chip Rev: %x.%x", value, ((cpu_data&C0_MAJREVMASK)>>C0_MAJREVSHIFT), ((cpu_data&C0_MINREVMASK)>>C0_MINREVSHIFT)); } } #endif #ifdef __FreeBSD__ if (cpu < proc_nthreaders()) { if ((value = (char *)malloc(64)) != NULL) { int len = 64; sysctlbyname("hw.machine_arch", value, &len, NULL, 0); } } #endif if (value) { ST(0) = sv_newmortal(); sv_setpv (ST(0), value); } else { ST(0) = &PL_sv_undef; } }