From c5c0539372aab1cc445641f6e52937dd45153311 Mon Sep 17 00:00:00 2001 From: Veselin Georgiev Date: Wed, 19 Nov 2008 16:27:31 +0000 Subject: [PATCH] * Support for stdin/stdout for (de)serializing cpu_raw_data_t; * set_warn_function renamed to cpuid_set_warn_function; * Updated Doxygen documentation: added manpage output, generic mainpage intro, default module named correctly; * Updated doxy libcpuid.h documentation, fixed some bugs; * Warnings are now printed to stderr by default, not stdout; * Some constants in AMD code did not reflected their meaning well, fixed; * The cpuid_tool utility thoroughly redesigned; now a multiple-function program, perhaps close to the finalized state. git-svn-id: https://svn.code.sf.net/p/libcpuid/code/HEAD/libcpuid@19 3b4be424-7ac5-41d7-8526-f4ddcb85d872 --- cpuid_tool/cpuid_tool.c | 595 ++++++++++++++++++++++++++++++++++++--- libcpuid/Doxyfile | 2 +- libcpuid/cpuid_main.c | 22 +- libcpuid/libcpuid.h | 102 ++++--- libcpuid/libcpuid.sym | 1 + libcpuid/libcpuid_util.c | 2 +- libcpuid/recog_amd.c | 13 +- 7 files changed, 638 insertions(+), 99 deletions(-) diff --git a/cpuid_tool/cpuid_tool.c b/cpuid_tool/cpuid_tool.c index c9d5b5d..9e63358 100644 --- a/cpuid_tool/cpuid_tool.c +++ b/cpuid_tool/cpuid_tool.c @@ -1,47 +1,562 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/** + * @File cpuid_tool.c + * @Date 2008-11-19 + * @Author Veselin Georgiev + * @Brief Command line interface to libcpuid + * + * This file is provides a direct interface to libcpuid. See the usage() + * function (or just run the program with the `--help' switch) for a short + * command line options reference. + * + * This file has several purposes: + * + * 1) When started with no arguments, the program outputs the RAW and decoded + * CPU data to files (`raw.txt' and `report.txt', respectively) - this is + * intended to be a dumb, doubleclicky tool for non-developer + * users, that can provide debug info about unrecognized processors to + * libcpuid developers. + * 2) When operated from the terminal with the `--report' option, it is a + * generic CPU-info utility. + * 3) Can be used in shell scripts, e.g. to get the name of the CPU, cache + * sizes, features, with query options like `--cache', `--brandstr', etc. + * 4) Finally, it serves to self-document libcpiud itself :) + */ #include +#include +#include #include "libcpuid.h" -int main(void) +/* Globals: */ +char raw_data_file[256] = ""; +char out_file[256] = ""; +typedef enum { + NEED_CPUID_PRESENT, + NEED_VENDOR_STR, + NEED_VENDOR_ID, + NEED_BRAND_STRING, + NEED_FAMILY, + NEED_MODEL, + NEED_STEPPING, + NEED_EXT_FAMILY, + NEED_EXT_MODEL, + NEED_NUM_CORES, + NEED_NUM_LOGICAL, + NEED_TOTAL_CPUS, + NEED_L1D_SIZE, + NEED_L1I_SIZE, + NEED_L2_SIZE, + NEED_L3_SIZE, + NEED_L1D_ASSOC, + NEED_L2_ASSOC, + NEED_L3_ASSOC, + NEED_L1D_CACHELINE, + NEED_L2_CACHELINE, + NEED_L3_CACHELINE, + NEED_CODENAME, + NEED_FEATURES, + NEED_CLOCK, + NEED_CLOCK_RDTSC, +} output_data_switch; + +int need_input = 0, + need_output = 0, + need_quiet = 0, + need_report = 0, + need_clockreport = 0, + need_timed_clockreport = 0, + need_verbose = 0, + need_version = 0; + +#define MAX_REQUESTS 32 +int num_requests = 0; +output_data_switch requests[MAX_REQUESTS]; + +FILE *fout; + + +const struct { output_data_switch sw; const char* synopsis; int ident_required; } +matchtable[] = { + { NEED_CPUID_PRESENT, "--cpuid" , 0}, + { NEED_VENDOR_STR , "--vendorstr" , 1}, + { NEED_VENDOR_ID , "--vendorid" , 1}, + { NEED_BRAND_STRING , "--brandstr" , 1}, + { NEED_FAMILY , "--family" , 1}, + { NEED_MODEL , "--model" , 1}, + { NEED_STEPPING , "--stepping" , 1}, + { NEED_EXT_FAMILY , "--extfamily" , 1}, + { NEED_EXT_MODEL , "--extmodel" , 1}, + { NEED_NUM_CORES , "--cores" , 1}, + { NEED_NUM_LOGICAL , "--logical" , 1}, + { NEED_TOTAL_CPUS , "--total-cpus" , 1}, + { NEED_L1D_SIZE , "--l1d-cache" , 1}, + { NEED_L1I_SIZE , "--l1i-cache" , 1}, + { NEED_L2_SIZE , "--cache" , 1}, + { NEED_L2_SIZE , "--l2-cache" , 1}, + { NEED_L3_SIZE , "--l3-cache" , 1}, + { NEED_L1D_ASSOC , "--l1d-assoc" , 1}, + { NEED_L2_ASSOC , "--l2-assoc" , 1}, + { NEED_L3_ASSOC , "--l3-assoc" , 1}, + { NEED_L1D_CACHELINE, "--l1d-cacheline", 1}, + { NEED_L2_CACHELINE , "--l2-cacheline" , 1}, + { NEED_L3_CACHELINE , "--l3-cacheline" , 1}, + { NEED_CODENAME , "--codename" , 1}, + { NEED_FEATURES , "--flags" , 1}, + { NEED_CLOCK , "--clock" , 0}, + { NEED_CLOCK_RDTSC , "--clock-rdtsc" , 1}, +}; + +const int sz_match = (sizeof(matchtable) / sizeof(matchtable[0])); + +/* functions */ + +static void usage(void) { - if (!cpuid_present()) { - printf("No CPUID on this CPU, sorry!\n"); - return -1; - } - printf("CPUID is present\n"); - struct cpu_raw_data_t raw; - struct cpu_id_t data; - if (cpuid_get_raw_data(&raw) < 0) - printf("Error obtaining raw data: %s\n", cpuid_error()); - if (cpu_identify(&raw, &data) < 0) - printf("Error identifying the CPU: %s\n", cpuid_error()); - printf("CPU Info:\n------------------\n"); - printf(" vendor_str : `%s'\n", data.vendor_str); - printf(" vendor id : %d\n", (int) data.vendor); - printf(" brand_str : `%s'\n", data.brand_str); - printf(" family : %d\n", data.family); - printf(" model : %d\n", data.model); - printf(" stepping : %d\n", data.stepping); - printf(" ext_family : %d\n", data.ext_family); - printf(" ext_model : %d\n", data.ext_model); - printf(" num_cores : %d\n", data.num_cores); - printf(" num_logical: %d\n", data.num_logical_cpus); - printf(" total_cpus : %d\n", data.total_cpus); - printf(" L1 D cache : %d KB\n", data.l1_data_cache); - printf(" L1 I cache : %d KB\n", data.l1_instruction_cache); - printf(" L2 cache : %d KB\n", data.l2_cache); - printf(" L3 cache : %d KB\n", data.l3_cache); - printf(" L1D assoc. : %d-way\n", data.l1_assoc); - printf(" L2 assoc. : %d-way\n", data.l2_assoc); - printf(" L3 assoc. : %d-way\n", data.l3_assoc); - printf(" L1D line sz: %d bytes\n", data.l1_cacheline); - printf(" L2 line sz : %d bytes\n", data.l2_cacheline); - printf(" L3 line sz : %d bytes\n", data.l3_cacheline); - printf(" code name : `%s'\n", data.cpu_codename); - printf(" features :"); - int i; - for (i = 0; i < NUM_CPU_FEATURES; i++) - if (data.flags[i]) - printf(" %s", cpu_feature_str(i)); + printf("Usage: cpuid_tool [options]\n\n"); + printf("Options:\n"); + printf(" -h,--help - Show this help\n"); + printf(" --load= - Load RAW CPUID data from file\n"); + printf(" --save= - Aquire RAW CPUID data and write it to file\n"); + printf(" --report,--all - Report all decoded CPU info (w/o clock)\n"); + printf(" --clock - in conjunction to --report: print CPU clock as well\n"); + printf(" --clock-rdtsc - same as --clock, but use RDTSC for clock detection\n"); + printf(" --quiet - disable warnings\n"); + printf(" --outfile= - redirect all output to this file, instead of stdout\n"); + printf(" --verbose - be extra verbose\n"); + printf(" --version - print library version\n"); printf("\n"); + printf("Query switches (generate 1 line of ouput per switch; in order of appearance):"); + + int line_fill = 80, l, i; + for (i = 0; i < sz_match; i++) { + l = (int) strlen(matchtable[i].synopsis); + if (line_fill + l > 76) { + line_fill = 2; + printf("\n "); + } + printf("%s", matchtable[i].synopsis); + if (i < sz_match - 1) { + line_fill += l + 2; + printf(", "); + } + } + printf("\n\n"); + printf("If `-' is used for , then stdin/stdout will be used instead of files.\n"); + printf("When no options are present, the program behaves as if it was invoked with\n"); + printf(" cpuid_tool \"--save=raw.txt --outfile=report.txt --report --verbose\"\n"); +} + +static int parse_cmdline(int argc, char** argv) +{ + #define xerror(msg)\ + fprintf(stderr, "Error: %s\n\n", msg); \ + fprintf(stderr, "Use -h to get a list of supported options\n"); \ + return -1; + if (argc == 1) { + /* Default command line options */ + need_output = 1; + strcpy(raw_data_file, "raw.txt"); + strcpy(out_file, "report.txt"); + need_report = 1; + need_verbose = 1; + return 1; + } + int i, j, recog; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + recog = 0; + if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { + usage(); + return 0; + } + if (!strncmp(arg, "--load=", 7)) { + if (need_input) { + xerror("Too many `--load' options!"); + } + if (need_output) { + xerror("Cannot have both `--load' and `--save' options!"); + } + if (strlen(arg) <= 7) { + xerror("--load: bad file specification!"); + } + need_input = 1; + strcpy(raw_data_file, arg + 7); + recog = 1; + } + if (!strncmp(arg, "--save=", 7)) { + if (need_output) { + xerror("Too many `--save' options!"); + } + if (need_input) { + xerror("Cannot have both `--load' and `--save' options!"); + } + if (strlen(arg) <= 7) { + xerror("--save: bad file specification!"); + } + need_output = 1; + strcpy(raw_data_file, arg + 7); + recog = 1; + } + if (!strncmp(arg, "--outfile=", 10)) { + if (strlen(arg) <= 10) { + xerror("--output: bad file specification!"); + } + strcpy(out_file, arg + 10); + recog = 1; + } + if (!strcmp(arg, "--report") || !strcmp(arg, "--all")) { + need_report = 1; + recog = 1; + } + if (!strcmp(arg, "--clock")) { + need_clockreport = 1; + recog = 1; + } + if (!strcmp(arg, "--clock-rdtsc")) { + need_timed_clockreport = 1; + recog = 1; + } + if (!strcmp(arg, "--quiet")) { + need_quiet = 1; + recog = 1; + } + if (!strcmp(arg, "--verbose")) { + need_verbose = 1; + recog = 1; + } + if (!strcmp(arg, "--version")) { + need_version = 1; + recog = 1; + } + for (j = 0; j < sz_match; j++) + if (!strcmp(arg, matchtable[j].synopsis)) { + if (num_requests >= MAX_REQUESTS) { + xerror("Too many requests!"); + } + requests[num_requests++] = matchtable[j].sw; + recog = 1; + break; + } + + if (!recog) { + fprintf(stderr, "Unrecognized option: `%s'\n\n", arg); + fprintf(stderr, "Use -h to get a list of supported options\n"); + return -1; + } + } + return 1; +} + +static void close_out(void) +{ + fclose(fout); +} + +static int check_need_raw_data(void) +{ + int i, j; + + if (need_output || need_report) return 1; + for (i = 0; i < num_requests; i++) { + for (j = 0; j < sz_match; j++) + if (requests[i] == matchtable[j].sw && + matchtable[j].ident_required) return 1; + } + return 0; +} + +static void print_info(output_data_switch query, struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int i; + switch (query) { + case NEED_CPUID_PRESENT: + fprintf(fout, "%d\n", cpuid_present()); + break; + case NEED_VENDOR_STR: + fprintf(fout, "%s\n", data->vendor_str); + break; + case NEED_VENDOR_ID: + fprintf(fout, "%d\n", data->vendor); + break; + case NEED_BRAND_STRING: + fprintf(fout, "%s\n", data->brand_str); + break; + case NEED_FAMILY: + fprintf(fout, "%d\n", data->family); + break; + case NEED_MODEL: + fprintf(fout, "%d\n", data->model); + break; + case NEED_STEPPING: + fprintf(fout, "%d\n", data->stepping); + break; + case NEED_EXT_FAMILY: + fprintf(fout, "%d\n", data->ext_family); + break; + case NEED_EXT_MODEL: + fprintf(fout, "%d\n", data->ext_model); + break; + case NEED_NUM_CORES: + fprintf(fout, "%d\n", data->num_cores); + break; + case NEED_NUM_LOGICAL: + fprintf(fout, "%d\n", data->num_logical_cpus); + break; + case NEED_TOTAL_CPUS: + fprintf(fout, "%d\n", data->total_cpus); + break; + case NEED_L1D_SIZE: + fprintf(fout, "%d\n", data->l1_data_cache); + break; + case NEED_L1I_SIZE: + fprintf(fout, "%d\n", data->l1_instruction_cache); + break; + case NEED_L2_SIZE: + fprintf(fout, "%d\n", data->l2_cache); + break; + case NEED_L3_SIZE: + fprintf(fout, "%d\n", data->l3_cache); + break; + case NEED_L1D_ASSOC: + fprintf(fout, "%d\n", data->l1_assoc); + break; + case NEED_L2_ASSOC: + fprintf(fout, "%d\n", data->l2_assoc); + break; + case NEED_L3_ASSOC: + fprintf(fout, "%d\n", data->l3_assoc); + break; + case NEED_L1D_CACHELINE: + fprintf(fout, "%d\n", data->l1_cacheline); + break; + case NEED_L2_CACHELINE: + fprintf(fout, "%d\n", data->l2_cacheline); + break; + case NEED_L3_CACHELINE: + fprintf(fout, "%d\n", data->l3_cacheline); + break; + case NEED_CODENAME: + fprintf(fout, "%s\n", data->cpu_codename); + break; + case NEED_FEATURES: + { + for (i = 0; i < NUM_CPU_FEATURES; i++) + if (data->flags[i]) + fprintf(fout, " %s", cpu_feature_str(i)); + fprintf(fout, "\n"); + break; + } + case NEED_CLOCK: + fprintf(fout, "%d\n", cpu_clock()); + break; + case NEED_CLOCK_RDTSC: + fprintf(fout, "%d\n", cpu_clock_measure(400, 1)); + break; + default: + fprintf(fout, "How did you get here?!?\n"); + break; + } +} + +int main(int argc, char** argv) +{ + int parseres = parse_cmdline(argc, argv); + int i, readres, writeres; + int only_clock_queries; + if (parseres != 1) + return parseres; + struct cpu_raw_data_t raw; + struct cpu_id_t data; + + /* In quiet mode, disable libcpuid warning messages: */ + if (need_quiet) + cpuid_set_warn_function(NULL); + + /* Redirect output, if necessary: */ + if (strcmp(out_file, "") && strcmp(out_file, "-")) { + fout = fopen(out_file, "wt"); + if (!fout) { + if (!need_quiet) + fprintf(stderr, "Cannot open `%s' for writing!\n", out_file); + return -1; + } + atexit(close_out); + } else { + fout = stdout; + } + + /* If requested, print library version: */ + if (need_version) + fprintf(fout, "%s\n", cpuid_lib_version()); + + if (need_input) { + /* We have a request to input raw CPUID data from file: */ + if (!strcmp(raw_data_file, "-")) + /* Input from stdin */ + readres = cpuid_deserialize_raw_data(&raw, ""); + else + /* Input from file */ + readres = cpuid_deserialize_raw_data(&raw, raw_data_file); + if (readres < 0) { + if (!need_quiet) { + fprintf(stderr, "Cannot deserialize raw data from "); + if (!strcmp(raw_data_file, "-")) + fprintf(stderr, "stdin\n"); + else + fprintf(stderr, "file `%s'\n", raw_data_file); + /* Print the error message */ + fprintf(stderr, "Error: %s\n", cpuid_error()); + } + return -1; + } + } else { + if (check_need_raw_data()) { + /* Try to obtain raw CPUID data from the CPU: */ + readres = cpuid_get_raw_data(&raw); + if (readres < 0) { + if (!need_quiet) { + fprintf(stderr, "Cannot obtain raw CPU data!\n"); + fprintf(stderr, "Error: %s\n", cpuid_error()); + } + return -1; + } + } + } + + /* Need to dump raw CPUID data to file: */ + if (need_output) { + if (need_verbose) + printf("Writing raw CPUID dump to `%s'\n", raw_data_file); + if (!strcmp(raw_data_file, "-")) + /* Serialize to stdout */ + writeres = cpuid_serialize_raw_data(&raw, ""); + else + /* Serialize to file */ + writeres = cpuid_serialize_raw_data(&raw, raw_data_file); + if (writeres < 0) { + if (!need_quiet) { + fprintf(stderr, "Cannot serialize raw data to "); + if (!strcmp(raw_data_file, "-")) + fprintf(stderr, "stdout\n"); + else + fprintf(stderr, "file `%s'\n", raw_data_file); + /* Print the error message */ + fprintf(stderr, "Error: %s\n", cpuid_error()); + } + return -1; + } + } + if (need_report) { + if (need_verbose) { + printf("Writing decoded CPU report to `%s'\n", out_file); + } + /* Write a thorough report of cpu_id_t structure to output (usually stdout) */ + fprintf(fout, "CPUID is present\n"); + /* + * Try CPU identification + * (this fill the `data' structure with decoded CPU features) + */ + if (cpu_identify(&raw, &data) < 0) + fprintf(fout, "Error identifying the CPU: %s\n", cpuid_error()); + + /* OK, now write what we have in `data'...: */ + fprintf(fout, "CPU Info:\n------------------\n"); + fprintf(fout, " vendor_str : `%s'\n", data.vendor_str); + fprintf(fout, " vendor id : %d\n", (int) data.vendor); + fprintf(fout, " brand_str : `%s'\n", data.brand_str); + fprintf(fout, " family : %d\n", data.family); + fprintf(fout, " model : %d\n", data.model); + fprintf(fout, " stepping : %d\n", data.stepping); + fprintf(fout, " ext_family : %d\n", data.ext_family); + fprintf(fout, " ext_model : %d\n", data.ext_model); + fprintf(fout, " num_cores : %d\n", data.num_cores); + fprintf(fout, " num_logical: %d\n", data.num_logical_cpus); + fprintf(fout, " total_cpus : %d\n", data.total_cpus); + fprintf(fout, " L1 D cache : %d KB\n", data.l1_data_cache); + fprintf(fout, " L1 I cache : %d KB\n", data.l1_instruction_cache); + fprintf(fout, " L2 cache : %d KB\n", data.l2_cache); + fprintf(fout, " L3 cache : %d KB\n", data.l3_cache); + fprintf(fout, " L1D assoc. : %d-way\n", data.l1_assoc); + fprintf(fout, " L2 assoc. : %d-way\n", data.l2_assoc); + fprintf(fout, " L3 assoc. : %d-way\n", data.l3_assoc); + fprintf(fout, " L1D line sz: %d bytes\n", data.l1_cacheline); + fprintf(fout, " L2 line sz : %d bytes\n", data.l2_cacheline); + fprintf(fout, " L3 line sz : %d bytes\n", data.l3_cacheline); + fprintf(fout, " code name : `%s'\n", data.cpu_codename); + fprintf(fout, " features :"); + /* + * Here we enumerate all CPU feature bits, and when a feature + * is present output its name: + */ + for (i = 0; i < NUM_CPU_FEATURES; i++) + if (data.flags[i]) + fprintf(fout, " %s", cpu_feature_str(i)); + fprintf(fout, "\n"); + + /* Is CPU clock info requested? */ + if (need_clockreport) { + if (need_timed_clockreport) { + /* Here we use the RDTSC-based routine */ + fprintf(fout, " cpu clock : %d MHz\n", + cpu_clock_measure(400, 1)); + } else { + /* Here we use the OS-provided info */ + fprintf(fout, " cpu clock : %d MHz\n", + cpu_clock()); + } + } + } + /* + * Check if we have any queries to process. + * We have to handle the case when `--clock' or `--clock-rdtsc' options + * are present. + * If in report mode, this will generate spurious output after the + * report, if not handled explicitly. + */ + only_clock_queries = 1; + for (i = 0; i < num_requests; i++) + if (requests[i] != NEED_CLOCK && requests[i] != NEED_CLOCK_RDTSC) { + only_clock_queries = 0; + break; + } + /* OK, process all queries. */ + if ((!need_report || !only_clock_queries) && num_requests > 0) { + if (cpu_identify(&raw, &data) < 0) { + if (!need_quiet) + fprintf(stderr, + "Error identifying the CPU: %s\n", + cpuid_error()); + return -1; + } + for (i = 0; i < num_requests; i++) + print_info(requests[i], &raw, &data); + } + return 0; } diff --git a/libcpuid/Doxyfile b/libcpuid/Doxyfile index 0e1e5cc..fac172b 100644 --- a/libcpuid/Doxyfile +++ b/libcpuid/Doxyfile @@ -862,7 +862,7 @@ RTF_EXTENSIONS_FILE = # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages -GENERATE_MAN = NO +GENERATE_MAN = YES # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be diff --git a/libcpuid/cpuid_main.c b/libcpuid/cpuid_main.c index a32805a..e6c1df3 100644 --- a/libcpuid/cpuid_main.c +++ b/libcpuid/cpuid_main.c @@ -38,7 +38,7 @@ static int _libcpiud_errno = ERR_OK; -static int set_error(cpuid_error_t err) +static int set_error(cpu_error_t err) { _libcpiud_errno = (int) err; return (int) err; @@ -291,7 +291,10 @@ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename) int i; FILE *f; - f = fopen(filename, "wt"); + if (!strcmp(filename, "")) + f = stdout; + else + f = fopen(filename, "wt"); if (!f) return set_error(ERR_OPEN); fprintf(f, "version=%s\n", VERSION); @@ -308,7 +311,8 @@ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename) data->intel_fn4[i][0], data->intel_fn4[i][1], data->intel_fn4[i][2], data->intel_fn4[i][3]); - fclose(f); + if (strcmp(filename, "")) + fclose(f); return set_error(ERR_OK); } @@ -325,7 +329,10 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename raw_data_t_constructor(data); - f = fopen(filename, "rt"); + if (!strcmp(filename, "")) + f = stdin; + else + f = fopen(filename, "rt"); if (!f) return set_error(ERR_OPEN); while (fgets(line, sizeof(line), f)) { ++cur_line; @@ -360,7 +367,8 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename } } - fclose(f); + if (strcmp(filename, "")) + fclose(f); return set_error(ERR_OK); } @@ -489,7 +497,7 @@ const char* cpu_feature_str(cpu_feature_t feature) const char* cpuid_error(void) { - const struct { cpuid_error_t error; const char *description; } + const struct { cpu_error_t error; const char *description; } matchtable[] = { { ERR_OK , "No error"}, { ERR_NO_CPUID , "CPUID instruction is not supported"}, @@ -513,7 +521,7 @@ const char* cpuid_lib_version(void) return VERSION; } -libcpuid_warn_fn_t set_warn_function(libcpuid_warn_fn_t new_fn) +libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t new_fn) { libcpuid_warn_fn_t ret = _warn_fun; _warn_fun = new_fn; diff --git a/libcpuid/libcpuid.h b/libcpuid/libcpuid.h index e6dfa7d..70cf422 100644 --- a/libcpuid/libcpuid.h +++ b/libcpuid/libcpuid.h @@ -23,6 +23,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef __LIBCPUID_H__ +#define __LIBCPUID_H__ /** * @File libcpuid.h * @Author Veselin Georgiev @@ -34,9 +36,21 @@ * 0.1.0 (2008-10-15): initial adaptation from wxfractgui sources */ -/** @defgroup libcpuid @{ */ -#ifndef __LIBCPUID_H__ -#define __LIBCPUID_H__ +/** @mainpage A simple libcpuid introduction + * + * LibCPUID provides CPU identification and access to the CPUID and RDTSC + * instructions on the x86. + *

+ * To execute CPUID, use \ref cpu_exec_cpuid
+ * To execute RDTSC, use \ref cpu_rdtsc
+ * To fetch the CPUID info needed for CPU identification, use + * \ref cpuid_get_raw_data
+ * To make sense of that data (decode, extract features), use \ref cpu_identify
+ *

+ */ + +/** @defgroup libcpuid LibCPUID + @{ */ /* Include some integer type specifications: */ #include "libcpuid_types.h" @@ -51,23 +65,22 @@ extern "C" { /** * @brief CPU vendor, as guessed from the Vendor String. */ -enum _cpu_vendor_t { - VENDOR_INTEL = 0, - VENDOR_AMD, - VENDOR_CYRIX, - VENDOR_NEXGEN, - VENDOR_TRANSMETA, - VENDOR_UMC, - VENDOR_CENTAUR, - VENDOR_RISE, - VENDOR_SIS, - VENDOR_NSC, +typedef enum { + VENDOR_INTEL = 0, /*!< Intel CPU */ + VENDOR_AMD, /*!< AMD CPU */ + VENDOR_CYRIX, /*!< Cyrix CPU */ + VENDOR_NEXGEN, /*!< NexGen CPU */ + VENDOR_TRANSMETA, /*!< Transmeta CPU */ + VENDOR_UMC, /*!< x86 CPU by UMC */ + VENDOR_CENTAUR, /*!< x86 CPU by IDT */ + VENDOR_RISE, /*!< x86 CPU by Rise Technology */ + VENDOR_SIS, /*!< x86 CPU by SiS */ + VENDOR_NSC, /*!< x86 CPU by National Semiconductor */ - NUM_CPU_VENDORS, + NUM_CPU_VENDORS, /*!< Valid CPU vendor ids: 0..NUM_CPU_VENDORS - 1 */ VENDOR_UNKNOWN = -1, -}; +} cpu_vendor_t; #define NUM_CPU_VENDORS NUM_CPU_VENDORS -typedef enum _cpu_vendor_t cpu_vendor_t; /** * @brief Contains just the raw CPUID data. @@ -209,7 +222,7 @@ struct cpu_id_t { * ... * struct cpu_raw_data_t raw; * struct cpu_id_t id; - * if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id)) { + * if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id) == 0) { * if (id.flags[CPU_FEATURE_SSE2]) { * // The CPU has SSE2... * ... @@ -221,7 +234,7 @@ struct cpu_id_t { * } * @endcode */ -enum _cpu_feature_t { +typedef enum { CPU_FEATURE_FPU = 0, /*!< Floating point unit */ CPU_FEATURE_VME, /*!< Virtual mode extension */ CPU_FEATURE_DE, /*!< Debugging extension */ @@ -307,13 +320,12 @@ enum _cpu_feature_t { CPU_FEATURE_CONSTANT_TSC, /*!< TSC ticks at constant rate */ // termination: NUM_CPU_FEATURES, -}; -typedef enum _cpu_feature_t cpu_feature_t; +} cpu_feature_t; /** * @brief Describes common library error codes */ -enum _cpuid_error_t { +typedef enum { ERR_OK = 0, /*!< "No error" */ ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */ ERR_NO_RDTSC = -2, /*!< "RDTSC instruction is not supported" */ @@ -322,8 +334,7 @@ enum _cpuid_error_t { ERR_BADFMT = -5, /*!< "Bad file format" */ ERR_NOT_IMP = -6, /*!< "Not implemented" */ ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */ -}; -typedef enum _cpuid_error_t cpuid_error_t; +} cpu_error_t; /** * @brief Internal structure, used in cpu_tsc_mark, cpu_tsc_unmark and @@ -363,8 +374,9 @@ void cpu_exec_cpuid_ext(uint32_t* regs); /** * @brief Obtains the raw CPUID data * @param data - a pointer to cpu_raw_data_t structure - * @returns zero if successful, and some negative number on error (@see cpuid_error_t) - * The error message can be obtained by calling cpuid_error + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t */ int cpuid_get_raw_data(struct cpu_raw_data_t* data); @@ -372,7 +384,7 @@ int cpuid_get_raw_data(struct cpu_raw_data_t* data); * @brief Writes the raw CPUID data to a text file * @param data - a pointer to cpu_raw_data_t structure * @param filename - the path of the file, where the serialized data should be - * written + * written. If empty, stdout will be used. * @note This is intended primarily for debugging. On some processor, which is * not currently supported or not completely recognized by cpu_identify, * one can still successfully get the raw data and write it to a file. @@ -381,8 +393,9 @@ int cpuid_get_raw_data(struct cpu_raw_data_t* data); * The file is simple text format of "something=value" pairs. Version info * is also written, but the format is not intended to be neither backward- * nor forward compatible. - * @returns zero if successful, and some negative number on error (@see cpuid_error_t) - * The error message can be obtained by calling cpuid_error + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t */ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); @@ -391,10 +404,12 @@ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); * @param data - a pointer to cpu_raw_data_t structure. The deserialized data will * be written here. * @param filename - the path of the file, containing the serialized raw data. + * If empty, stdin will be used. * @note This function may fail, if the file is created by different version of * the library. Also, see the notes on cpuid_serialize_raw_data. - * @returns zero if successful, and some negative number on error (@see cpuid_error_t) - * The error message can be obtained by calling cpuid_error + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t */ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename); @@ -417,8 +432,9 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename * performance significantly. Specifically, avoid writing "cpu feature * checker" wrapping function, which calls cpu_identify and returns the * value of some flag, if that function is going to be called frequently. - * @returns zero if successful, and some negative number on error (@see cpuid_error_t) - * The error message can be obtained by calling cpuid_error + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t */ int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data); @@ -437,6 +453,7 @@ const char* cpu_feature_str(cpu_feature_t feature); * libcpuid stores an `errno'-style error status, whose description * can be obtained with this function. * @note This function is not thread-safe + * @see cpu_error_t */ const char* cpuid_error(void); @@ -469,11 +486,12 @@ void cpu_rdtsc(uint64_t* result); /** * @brief Store TSC and timing info * - * This function stores the current TSC value (@see cpu_rdtsc) and current + * This function stores the current TSC value and current * time info from a precise OS-specific clock source in the cpu_mark_t * structure. The sys_clock field contains time with microsecond resolution. * The values can later be used to measure time intervals, number of clocks, * FPU frequency, etc. + * @see cpu_rdtsc * * @param mark [out] - a pointer to a cpu_mark_t structure */ @@ -526,10 +544,10 @@ int cpu_clock_by_mark(struct cpu_mark_t* mark); * @brief Returns the CPU clock, as reported by the OS * * This function uses OS-specific functions to obtain the CPU clock. It may - * differ from the true clock for several reasons: + * differ from the true clock for several reasons:

* * i) The CPU might be in some power saving state, while the OS reports its - * full-power frequency, or vice-versa. + * full-power frequency, or vice-versa.
* ii) In some cases you can raise or lower the CPU frequency with overclocking * utilities and the OS will not notice. * @@ -552,18 +570,18 @@ int cpu_clock_by_os(void); * but 100ms should be enough for most purposes. * * While this will calculate the CPU frequency correctly in most cases, there are - * several reasons why it might be incorrect: + * several reasons why it might be incorrect:
* * i) RDTSC doesn't guarantee it will run at the same clock as the CPU. * Apparently there aren't CPUs at the moment, but still, there's no - * guarantee. + * guarantee.
* ii) The CPU might be in a low-frequency power saving mode, and the CPU * might be switched to higher frequency at any time. If this happens * during the measurement, the result can be anywhere between the * low and high frequencies. Also, if you're interested in the * high frequency value only, this function might return the low one - * instead. - * iii) On SMP systems exhibiting TSC drift (@see cpu_rdtsc) + * instead.
+ * iii) On SMP systems exhibiting TSC drift (see \ref cpu_rdtsc) * * the quad_check option will run four consecutive measurements and * then return the average of the two most-consistent results. The total @@ -606,7 +624,7 @@ typedef void (*libcpuid_warn_fn_t) (const char *msg); * @brief Sets the warning print function * * In some cases, the internal libcpuid machinery would like to emit useful - * debug warnings. By default, these warnings are written to stdout. However, + * debug warnings. By default, these warnings are written to stderr. However, * you can set a custom function that will receive those warnings. * * @param warn_fun - the warning function you want to set. If NULL, warnings @@ -615,7 +633,7 @@ typedef void (*libcpuid_warn_fn_t) (const char *msg); * @returns the current warning function. You can use the return value to * keep the previous warning function and restore it at your discretion. */ -libcpuid_warn_fn_t set_warn_function(libcpuid_warn_fn_t warn_fun); +libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t warn_fun); #ifdef __cplusplus }; // extern "C" diff --git a/libcpuid/libcpuid.sym b/libcpuid/libcpuid.sym index 4fa33a0..feabd76 100644 --- a/libcpuid/libcpuid.sym +++ b/libcpuid/libcpuid.sym @@ -15,3 +15,4 @@ cpu_clock_by_os cpu_clock_measure cpu_clock cpuid_lib_version +cpuid_set_warn_function diff --git a/libcpuid/libcpuid_util.c b/libcpuid/libcpuid_util.c index dc3e4b2..10de3cf 100644 --- a/libcpuid/libcpuid_util.c +++ b/libcpuid/libcpuid_util.c @@ -41,7 +41,7 @@ void match_features(const struct feature_map_t* matchtable, int count, uint32_t static void default_warn(const char *msg) { - printf("%s", msg); + fprintf(stderr, "%s", msg); } libcpuid_warn_fn_t _warn_fun = default_warn; diff --git a/libcpuid/recog_amd.c b/libcpuid/recog_amd.c index d1b9fb5..eb7f35e 100644 --- a/libcpuid/recog_amd.c +++ b/libcpuid/recog_amd.c @@ -39,8 +39,7 @@ enum _amd_code_t { ATHLON_XP_BARTON, ATHLON_XP_M, ATHLON_XP_M_LV, - ATHLON_64, - ATHLON_64_MANCHESTER, + ATHLON, ATHLON_MP, MOBILE_ATHLON64, ATHLON_FX, @@ -102,7 +101,7 @@ const struct match_entry_t cpudb_amd[] = { { 6, 4, -1, -1, -1, NO_CODE, "Athlon (ThunderBird)" }, { 6, 6, -1, -1, -1, NO_CODE, "Unknown Athlon" }, - { 6, 6, -1, -1, -1, ATHLON_64, "Athlon (Palomino)" }, + { 6, 6, -1, -1, -1, ATHLON, "Athlon (Palomino)" }, { 6, 6, -1, -1, -1, ATHLON_MP, "Athlon MP (Palomino)" }, { 6, 6, -1, -1, -1, DURON, "Duron (Palomino)" }, { 6, 6, -1, -1, -1, ATHLON_XP, "Athlon XP" }, @@ -111,7 +110,7 @@ const struct match_entry_t cpudb_amd[] = { { 6, 7, -1, -1, -1, DURON, "Duron (Morgan)" }, { 6, 8, -1, -1, -1, NO_CODE, "Athlon XP" }, - { 6, 8, -1, -1, -1, ATHLON_64, "Athlon XP" }, + { 6, 8, -1, -1, -1, ATHLON, "Athlon XP" }, { 6, 8, -1, -1, -1, ATHLON_XP, "Athlon XP" }, { 6, 8, -1, -1, -1, DURON, "Duron (Applebred)" }, { 6, 8, -1, -1, -1, SEMPRON, "Sempron (Thoroughbred)" }, @@ -122,7 +121,7 @@ const struct match_entry_t cpudb_amd[] = { { 6, 8, -1, -1, -1, ATHLON_XP_M_LV, "Mobile Athlon (Thoroughbred)" }, { 6, 10, -1, -1, -1, NO_CODE, "Athlon XP (Barton)" }, - { 6, 10, -1, -1, -1, ATHLON_64, "Athlon XP (Barton)" }, + { 6, 10, -1, -1, -1, ATHLON, "Athlon XP (Barton)" }, { 6, 10, -1, -1, -1, ATHLON_XP_BARTON, "Athlon XP (Barton)" }, { 6, 10, -1, -1, -1, SEMPRON, "Sempron (Barton)" }, { 6, 10, -1, -1, -1, SEMPRON_64_256K, "Sempron (Barton)" }, @@ -280,7 +279,7 @@ static amd_code_t decode_amd_codename_part1(const char *bs) if (strstr(bs, "Athlon(tm) XP")) return ATHLON_XP; if (strstr(bs, "Athlon(tm) MP")) return ATHLON_MP; if (strstr(bs, "Duron")) return DURON; - if (strstr(bs, "Athlon")) return ATHLON_64; + if (strstr(bs, "Athlon")) return ATHLON; } return NO_CODE; @@ -290,8 +289,6 @@ static void decode_amd_codename(struct cpu_raw_data_t* raw, struct cpu_id_t* dat { amd_code_t code = decode_amd_codename_part1(data->brand_str); - if (code == ATHLON_64 && data->l2_cache == 512) - code = ATHLON_64_MANCHESTER; if (code == ATHLON_XP && data->l2_cache == 512) code = ATHLON_XP_BARTON; if (code == ATHLON_64_512K && data->l2_cache > 512)