1
0
Fork 0
mirror of https://github.com/anrieff/libcpuid synced 2025-07-02 14:04:15 +00:00

* 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
This commit is contained in:
Veselin Georgiev 2008-11-19 16:27:31 +00:00
parent 5c348fd6bd
commit c5c0539372
7 changed files with 638 additions and 99 deletions

View file

@ -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 <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libcpuid.h" #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("Usage: cpuid_tool [options]\n\n");
printf("No CPUID on this CPU, sorry!\n"); printf("Options:\n");
printf(" -h,--help - Show this help\n");
printf(" --load=<file> - Load RAW CPUID data from file\n");
printf(" --save=<file> - 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=<file> - 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 <file>, 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;
} }
printf("CPUID is present\n"); }
struct cpu_raw_data_t raw; return 1;
struct cpu_id_t data; }
if (cpuid_get_raw_data(&raw) < 0)
printf("Error obtaining raw data: %s\n", cpuid_error()); static void close_out(void)
if (cpu_identify(&raw, &data) < 0) {
printf("Error identifying the CPU: %s\n", cpuid_error()); fclose(fout);
printf("CPU Info:\n------------------\n"); }
printf(" vendor_str : `%s'\n", data.vendor_str);
printf(" vendor id : %d\n", (int) data.vendor); static int check_need_raw_data(void)
printf(" brand_str : `%s'\n", data.brand_str); {
printf(" family : %d\n", data.family); int i, j;
printf(" model : %d\n", data.model);
printf(" stepping : %d\n", data.stepping); if (need_output || need_report) return 1;
printf(" ext_family : %d\n", data.ext_family); for (i = 0; i < num_requests; i++) {
printf(" ext_model : %d\n", data.ext_model); for (j = 0; j < sz_match; j++)
printf(" num_cores : %d\n", data.num_cores); if (requests[i] == matchtable[j].sw &&
printf(" num_logical: %d\n", data.num_logical_cpus); matchtable[j].ident_required) return 1;
printf(" total_cpus : %d\n", data.total_cpus); }
printf(" L1 D cache : %d KB\n", data.l1_data_cache); return 0;
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); static void print_info(output_data_switch query, struct cpu_raw_data_t* raw,
printf(" L1D assoc. : %d-way\n", data.l1_assoc); struct cpu_id_t* data)
printf(" L2 assoc. : %d-way\n", data.l2_assoc); {
printf(" L3 assoc. : %d-way\n", data.l3_assoc); int i;
printf(" L1D line sz: %d bytes\n", data.l1_cacheline); switch (query) {
printf(" L2 line sz : %d bytes\n", data.l2_cacheline); case NEED_CPUID_PRESENT:
printf(" L3 line sz : %d bytes\n", data.l3_cacheline); fprintf(fout, "%d\n", cpuid_present());
printf(" code name : `%s'\n", data.cpu_codename); break;
printf(" features :"); case NEED_VENDOR_STR:
int i; fprintf(fout, "%s\n", data->vendor_str);
for (i = 0; i < NUM_CPU_FEATURES; i++) break;
if (data.flags[i]) case NEED_VENDOR_ID:
printf(" %s", cpu_feature_str(i)); fprintf(fout, "%d\n", data->vendor);
printf("\n"); 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; return 0;
} }

View file

@ -862,7 +862,7 @@ RTF_EXTENSIONS_FILE =
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will # If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages # generate man pages
GENERATE_MAN = NO GENERATE_MAN = YES
# The MAN_OUTPUT tag is used to specify where the man pages will be put. # 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 # If a relative path is entered the value of OUTPUT_DIRECTORY will be

View file

@ -38,7 +38,7 @@
static int _libcpiud_errno = ERR_OK; 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; _libcpiud_errno = (int) err;
return (int) err; return (int) err;
@ -291,6 +291,9 @@ int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename)
int i; int i;
FILE *f; FILE *f;
if (!strcmp(filename, ""))
f = stdout;
else
f = fopen(filename, "wt"); f = fopen(filename, "wt");
if (!f) return set_error(ERR_OPEN); if (!f) return set_error(ERR_OPEN);
@ -308,6 +311,7 @@ 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][0], data->intel_fn4[i][1],
data->intel_fn4[i][2], data->intel_fn4[i][3]); data->intel_fn4[i][2], data->intel_fn4[i][3]);
if (strcmp(filename, ""))
fclose(f); fclose(f);
return set_error(ERR_OK); return set_error(ERR_OK);
} }
@ -325,6 +329,9 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename
raw_data_t_constructor(data); raw_data_t_constructor(data);
if (!strcmp(filename, ""))
f = stdin;
else
f = fopen(filename, "rt"); f = fopen(filename, "rt");
if (!f) return set_error(ERR_OPEN); if (!f) return set_error(ERR_OPEN);
while (fgets(line, sizeof(line), f)) { while (fgets(line, sizeof(line), f)) {
@ -360,6 +367,7 @@ int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename
} }
} }
if (strcmp(filename, ""))
fclose(f); fclose(f);
return set_error(ERR_OK); return set_error(ERR_OK);
} }
@ -489,7 +497,7 @@ const char* cpu_feature_str(cpu_feature_t feature)
const char* cpuid_error(void) const char* cpuid_error(void)
{ {
const struct { cpuid_error_t error; const char *description; } const struct { cpu_error_t error; const char *description; }
matchtable[] = { matchtable[] = {
{ ERR_OK , "No error"}, { ERR_OK , "No error"},
{ ERR_NO_CPUID , "CPUID instruction is not supported"}, { ERR_NO_CPUID , "CPUID instruction is not supported"},
@ -513,7 +521,7 @@ const char* cpuid_lib_version(void)
return VERSION; 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; libcpuid_warn_fn_t ret = _warn_fun;
_warn_fun = new_fn; _warn_fun = new_fn;

View file

@ -23,6 +23,8 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef __LIBCPUID_H__
#define __LIBCPUID_H__
/** /**
* @File libcpuid.h * @File libcpuid.h
* @Author Veselin Georgiev * @Author Veselin Georgiev
@ -34,9 +36,21 @@
* 0.1.0 (2008-10-15): initial adaptation from wxfractgui sources * 0.1.0 (2008-10-15): initial adaptation from wxfractgui sources
*/ */
/** @defgroup libcpuid @{ */ /** @mainpage A simple libcpuid introduction
#ifndef __LIBCPUID_H__ *
#define __LIBCPUID_H__ * LibCPUID provides CPU identification and access to the CPUID and RDTSC
* instructions on the x86.
* <p>
* To execute CPUID, use \ref cpu_exec_cpuid <br>
* To execute RDTSC, use \ref cpu_rdtsc <br>
* To fetch the CPUID info needed for CPU identification, use
* \ref cpuid_get_raw_data <br>
* To make sense of that data (decode, extract features), use \ref cpu_identify <br>
* </p>
*/
/** @defgroup libcpuid LibCPUID
@{ */
/* Include some integer type specifications: */ /* Include some integer type specifications: */
#include "libcpuid_types.h" #include "libcpuid_types.h"
@ -51,23 +65,22 @@ extern "C" {
/** /**
* @brief CPU vendor, as guessed from the Vendor String. * @brief CPU vendor, as guessed from the Vendor String.
*/ */
enum _cpu_vendor_t { typedef enum {
VENDOR_INTEL = 0, VENDOR_INTEL = 0, /*!< Intel CPU */
VENDOR_AMD, VENDOR_AMD, /*!< AMD CPU */
VENDOR_CYRIX, VENDOR_CYRIX, /*!< Cyrix CPU */
VENDOR_NEXGEN, VENDOR_NEXGEN, /*!< NexGen CPU */
VENDOR_TRANSMETA, VENDOR_TRANSMETA, /*!< Transmeta CPU */
VENDOR_UMC, VENDOR_UMC, /*!< x86 CPU by UMC */
VENDOR_CENTAUR, VENDOR_CENTAUR, /*!< x86 CPU by IDT */
VENDOR_RISE, VENDOR_RISE, /*!< x86 CPU by Rise Technology */
VENDOR_SIS, VENDOR_SIS, /*!< x86 CPU by SiS */
VENDOR_NSC, 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, VENDOR_UNKNOWN = -1,
}; } cpu_vendor_t;
#define NUM_CPU_VENDORS NUM_CPU_VENDORS #define NUM_CPU_VENDORS NUM_CPU_VENDORS
typedef enum _cpu_vendor_t cpu_vendor_t;
/** /**
* @brief Contains just the raw CPUID data. * @brief Contains just the raw CPUID data.
@ -209,7 +222,7 @@ struct cpu_id_t {
* ... * ...
* struct cpu_raw_data_t raw; * struct cpu_raw_data_t raw;
* struct cpu_id_t id; * 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]) { * if (id.flags[CPU_FEATURE_SSE2]) {
* // The CPU has SSE2... * // The CPU has SSE2...
* ... * ...
@ -221,7 +234,7 @@ struct cpu_id_t {
* } * }
* @endcode * @endcode
*/ */
enum _cpu_feature_t { typedef enum {
CPU_FEATURE_FPU = 0, /*!< Floating point unit */ CPU_FEATURE_FPU = 0, /*!< Floating point unit */
CPU_FEATURE_VME, /*!< Virtual mode extension */ CPU_FEATURE_VME, /*!< Virtual mode extension */
CPU_FEATURE_DE, /*!< Debugging extension */ CPU_FEATURE_DE, /*!< Debugging extension */
@ -307,13 +320,12 @@ enum _cpu_feature_t {
CPU_FEATURE_CONSTANT_TSC, /*!< TSC ticks at constant rate */ CPU_FEATURE_CONSTANT_TSC, /*!< TSC ticks at constant rate */
// termination: // termination:
NUM_CPU_FEATURES, NUM_CPU_FEATURES,
}; } cpu_feature_t;
typedef enum _cpu_feature_t cpu_feature_t;
/** /**
* @brief Describes common library error codes * @brief Describes common library error codes
*/ */
enum _cpuid_error_t { typedef enum {
ERR_OK = 0, /*!< "No error" */ ERR_OK = 0, /*!< "No error" */
ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */ ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */
ERR_NO_RDTSC = -2, /*!< "RDTSC 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_BADFMT = -5, /*!< "Bad file format" */
ERR_NOT_IMP = -6, /*!< "Not implemented" */ ERR_NOT_IMP = -6, /*!< "Not implemented" */
ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */ ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */
}; } cpu_error_t;
typedef enum _cpuid_error_t cpuid_error_t;
/** /**
* @brief Internal structure, used in cpu_tsc_mark, cpu_tsc_unmark and * @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 * @brief Obtains the raw CPUID data
* @param data - a pointer to cpu_raw_data_t structure * @param data - a pointer to cpu_raw_data_t structure
* @returns zero if successful, and some negative number on error (@see cpuid_error_t) * @returns zero if successful, and some negative number on error.
* The error message can be obtained by calling cpuid_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); 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 * @brief Writes the raw CPUID data to a text file
* @param data - a pointer to cpu_raw_data_t structure * @param data - a pointer to cpu_raw_data_t structure
* @param filename - the path of the file, where the serialized data should be * @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 * @note This is intended primarily for debugging. On some processor, which is
* not currently supported or not completely recognized by cpu_identify, * not currently supported or not completely recognized by cpu_identify,
* one can still successfully get the raw data and write it to a file. * 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 * 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- * is also written, but the format is not intended to be neither backward-
* nor forward compatible. * nor forward compatible.
* @returns zero if successful, and some negative number on error (@see cpuid_error_t) * @returns zero if successful, and some negative number on error.
* The error message can be obtained by calling cpuid_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); 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 * @param data - a pointer to cpu_raw_data_t structure. The deserialized data will
* be written here. * be written here.
* @param filename - the path of the file, containing the serialized raw data. * @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 * @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. * 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) * @returns zero if successful, and some negative number on error.
* The error message can be obtained by calling cpuid_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); 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 * performance significantly. Specifically, avoid writing "cpu feature
* checker" wrapping function, which calls cpu_identify and returns the * checker" wrapping function, which calls cpu_identify and returns the
* value of some flag, if that function is going to be called frequently. * 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) * @returns zero if successful, and some negative number on error.
* The error message can be obtained by calling cpuid_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); 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 * libcpuid stores an `errno'-style error status, whose description
* can be obtained with this function. * can be obtained with this function.
* @note This function is not thread-safe * @note This function is not thread-safe
* @see cpu_error_t
*/ */
const char* cpuid_error(void); const char* cpuid_error(void);
@ -469,11 +486,12 @@ void cpu_rdtsc(uint64_t* result);
/** /**
* @brief Store TSC and timing info * @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 * time info from a precise OS-specific clock source in the cpu_mark_t
* structure. The sys_clock field contains time with microsecond resolution. * structure. The sys_clock field contains time with microsecond resolution.
* The values can later be used to measure time intervals, number of clocks, * The values can later be used to measure time intervals, number of clocks,
* FPU frequency, etc. * FPU frequency, etc.
* @see cpu_rdtsc
* *
* @param mark [out] - a pointer to a cpu_mark_t structure * @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 * @brief Returns the CPU clock, as reported by the OS
* *
* This function uses OS-specific functions to obtain the CPU clock. It may * 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:<br><br>
* *
* i) The CPU might be in some power saving state, while the OS reports its * 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.<br>
* ii) In some cases you can raise or lower the CPU frequency with overclocking * ii) In some cases you can raise or lower the CPU frequency with overclocking
* utilities and the OS will not notice. * 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. * but 100ms should be enough for most purposes.
* *
* While this will calculate the CPU frequency correctly in most cases, there are * 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:<br>
* *
* i) RDTSC doesn't guarantee it will run at the same clock as the CPU. * 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 * Apparently there aren't CPUs at the moment, but still, there's no
* guarantee. * guarantee.<br>
* ii) The CPU might be in a low-frequency power saving mode, and the CPU * 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 * might be switched to higher frequency at any time. If this happens
* during the measurement, the result can be anywhere between the * during the measurement, the result can be anywhere between the
* low and high frequencies. Also, if you're interested in the * low and high frequencies. Also, if you're interested in the
* high frequency value only, this function might return the low one * high frequency value only, this function might return the low one
* instead. * instead.<br>
* iii) On SMP systems exhibiting TSC drift (@see cpu_rdtsc) * iii) On SMP systems exhibiting TSC drift (see \ref cpu_rdtsc)
* *
* the quad_check option will run four consecutive measurements and * the quad_check option will run four consecutive measurements and
* then return the average of the two most-consistent results. The total * 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 * @brief Sets the warning print function
* *
* In some cases, the internal libcpuid machinery would like to emit useful * 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. * you can set a custom function that will receive those warnings.
* *
* @param warn_fun - the warning function you want to set. If NULL, 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 * @returns the current warning function. You can use the return value to
* keep the previous warning function and restore it at your discretion. * 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 #ifdef __cplusplus
}; // extern "C" }; // extern "C"

View file

@ -15,3 +15,4 @@ cpu_clock_by_os
cpu_clock_measure cpu_clock_measure
cpu_clock cpu_clock
cpuid_lib_version cpuid_lib_version
cpuid_set_warn_function

View file

@ -41,7 +41,7 @@ void match_features(const struct feature_map_t* matchtable, int count, uint32_t
static void default_warn(const char *msg) static void default_warn(const char *msg)
{ {
printf("%s", msg); fprintf(stderr, "%s", msg);
} }
libcpuid_warn_fn_t _warn_fun = default_warn; libcpuid_warn_fn_t _warn_fun = default_warn;

View file

@ -39,8 +39,7 @@ enum _amd_code_t {
ATHLON_XP_BARTON, ATHLON_XP_BARTON,
ATHLON_XP_M, ATHLON_XP_M,
ATHLON_XP_M_LV, ATHLON_XP_M_LV,
ATHLON_64, ATHLON,
ATHLON_64_MANCHESTER,
ATHLON_MP, ATHLON_MP,
MOBILE_ATHLON64, MOBILE_ATHLON64,
ATHLON_FX, ATHLON_FX,
@ -102,7 +101,7 @@ const struct match_entry_t cpudb_amd[] = {
{ 6, 4, -1, -1, -1, NO_CODE, "Athlon (ThunderBird)" }, { 6, 4, -1, -1, -1, NO_CODE, "Athlon (ThunderBird)" },
{ 6, 6, -1, -1, -1, NO_CODE, "Unknown Athlon" }, { 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, ATHLON_MP, "Athlon MP (Palomino)" },
{ 6, 6, -1, -1, -1, DURON, "Duron (Palomino)" }, { 6, 6, -1, -1, -1, DURON, "Duron (Palomino)" },
{ 6, 6, -1, -1, -1, ATHLON_XP, "Athlon XP" }, { 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, 7, -1, -1, -1, DURON, "Duron (Morgan)" },
{ 6, 8, -1, -1, -1, NO_CODE, "Athlon XP" }, { 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, ATHLON_XP, "Athlon XP" },
{ 6, 8, -1, -1, -1, DURON, "Duron (Applebred)" }, { 6, 8, -1, -1, -1, DURON, "Duron (Applebred)" },
{ 6, 8, -1, -1, -1, SEMPRON, "Sempron (Thoroughbred)" }, { 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, 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, 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, ATHLON_XP_BARTON, "Athlon XP (Barton)" },
{ 6, 10, -1, -1, -1, SEMPRON, "Sempron (Barton)" }, { 6, 10, -1, -1, -1, SEMPRON, "Sempron (Barton)" },
{ 6, 10, -1, -1, -1, SEMPRON_64_256K, "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) XP")) return ATHLON_XP;
if (strstr(bs, "Athlon(tm) MP")) return ATHLON_MP; if (strstr(bs, "Athlon(tm) MP")) return ATHLON_MP;
if (strstr(bs, "Duron")) return DURON; if (strstr(bs, "Duron")) return DURON;
if (strstr(bs, "Athlon")) return ATHLON_64; if (strstr(bs, "Athlon")) return ATHLON;
} }
return NO_CODE; 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); 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) if (code == ATHLON_XP && data->l2_cache == 512)
code = ATHLON_XP_BARTON; code = ATHLON_XP_BARTON;
if (code == ATHLON_64_512K && data->l2_cache > 512) if (code == ATHLON_64_512K && data->l2_cache > 512)