driconf: Restore the ability to override driconf with the environment.

I distinctly remember testing this during development, yet clearly deleted
the code.

I pulled the parseValue code back up to the top where it used to be since
we have to use it, rather than just adding a prototype.

Fixes: 8a05d6ffc6 ("driconf: Make the driver's declarations be structs instead of XML.")
Reviewed-by: Michel Dänzer <mdaenzer@redhat.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7012>
This commit is contained in:
Eric Anholt 2020-10-05 10:18:49 -07:00 committed by Marge Bot
parent 455bfecdff
commit 7fb4ab9ec1
1 changed files with 370 additions and 352 deletions

View File

@ -61,262 +61,6 @@
#define PATH_MAX 4096
#endif
/** \brief Find an option in an option cache with the name as key */
static uint32_t
findOption(const driOptionCache *cache, const char *name)
{
uint32_t len = strlen(name);
uint32_t size = 1 << cache->tableSize, mask = size - 1;
uint32_t hash = 0;
uint32_t i, shift;
/* compute a hash from the variable length name */
for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
hash += (uint32_t)name[i] << shift;
hash *= hash;
hash = (hash >> (16-cache->tableSize/2)) & mask;
/* this is just the starting point of the linear search for the option */
for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
/* if we hit an empty entry then the option is not defined (yet) */
if (cache->info[hash].name == 0)
break;
else if (!strcmp(name, cache->info[hash].name))
break;
}
/* this assertion fails if the hash table is full */
assert (i < size);
return hash;
}
/** \brief Like strdup with error checking. */
#define XSTRDUP(dest,source) do { \
if (!(dest = strdup(source))) { \
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
abort(); \
} \
} while (0)
/** \brief Check if a value is in info->range. */
UNUSED static bool
checkValue(const driOptionValue *v, const driOptionInfo *info)
{
switch (info->type) {
case DRI_ENUM: /* enum is just a special integer */
case DRI_INT:
return (info->range.start._int == info->range.end._int ||
(v->_int >= info->range.start._int &&
v->_int <= info->range.end._int));
case DRI_FLOAT:
return (info->range.start._float == info->range.end._float ||
(v->_float >= info->range.start._float &&
v->_float <= info->range.end._float));
default:
return true;
}
}
void
driParseOptionInfo(driOptionCache *info,
const driOptionDescription *configOptions,
unsigned numOptions)
{
/* Make the hash table big enough to fit more than the maximum number of
* config options we've ever seen in a driver.
*/
info->tableSize = 6;
info->info = calloc(1 << info->tableSize, sizeof(driOptionInfo));
info->values = calloc(1 << info->tableSize, sizeof(driOptionValue));
if (info->info == NULL || info->values == NULL) {
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
abort();
}
UNUSED bool in_section = false;
for (int o = 0; o < numOptions; o++) {
const driOptionDescription *opt = &configOptions[o];
if (opt->info.type == DRI_SECTION) {
in_section = true;
continue;
}
/* for driconf xml generation, options must always be preceded by a
* DRI_CONF_SECTION
*/
assert(in_section);
const char *name = opt->info.name;
int i = findOption(info, name);
driOptionInfo *optinfo = &info->info[i];
driOptionValue *optval = &info->values[i];
assert(!optinfo->name); /* No duplicate options in your list. */
optinfo->type = opt->info.type;
optinfo->range = opt->info.range;
XSTRDUP(optinfo->name, name);
switch (opt->info.type) {
case DRI_BOOL:
optval->_bool = opt->value._bool;
break;
case DRI_INT:
case DRI_ENUM:
optval->_int = opt->value._int;
break;
case DRI_FLOAT:
optval->_float = opt->value._float;
break;
case DRI_STRING:
XSTRDUP(optval->_string, opt->value._string);
break;
case DRI_SECTION:
unreachable("handled above");
}
/* Built-in default values should always be valid. */
assert(checkValue(optval, optinfo));
}
}
char *
driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions)
{
char *str = ralloc_strdup(NULL,
"<?xml version=\"1.0\" standalone=\"yes\"?>\n" \
"<!DOCTYPE driinfo [\n" \
" <!ELEMENT driinfo (section*)>\n" \
" <!ELEMENT section (description+, option+)>\n" \
" <!ELEMENT description (enum*)>\n" \
" <!ATTLIST description lang CDATA #FIXED \"en\"\n" \
" text CDATA #REQUIRED>\n" \
" <!ELEMENT option (description+)>\n" \
" <!ATTLIST option name CDATA #REQUIRED\n" \
" type (bool|enum|int|float) #REQUIRED\n" \
" default CDATA #REQUIRED\n" \
" valid CDATA #IMPLIED>\n" \
" <!ELEMENT enum EMPTY>\n" \
" <!ATTLIST enum value CDATA #REQUIRED\n" \
" text CDATA #REQUIRED>\n" \
"]>" \
"<driinfo>\n");
bool in_section = false;
for (int o = 0; o < numOptions; o++) {
const driOptionDescription *opt = &configOptions[o];
const char *name = opt->info.name;
const char *types[] = {
[DRI_BOOL] = "bool",
[DRI_INT] = "int",
[DRI_FLOAT] = "float",
[DRI_ENUM] = "enum",
[DRI_STRING] = "string",
};
if (opt->info.type == DRI_SECTION) {
if (in_section)
ralloc_asprintf_append(&str, " </section>\n");
ralloc_asprintf_append(&str,
" <section>\n"
" <description lang=\"en\" text=\"%s\"/>\n",
opt->desc);
in_section = true;
continue;
}
ralloc_asprintf_append(&str,
" <option name=\"%s\" type=\"%s\" default=\"",
name,
types[opt->info.type]);
switch (opt->info.type) {
case DRI_BOOL:
ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false");
break;
case DRI_INT:
case DRI_ENUM:
ralloc_asprintf_append(&str, "%d", opt->value._int);
break;
case DRI_FLOAT:
ralloc_asprintf_append(&str, "%f", opt->value._float);
break;
case DRI_STRING:
ralloc_asprintf_append(&str, "%s", opt->value._string);
break;
case DRI_SECTION:
unreachable("handled above");
break;
}
ralloc_asprintf_append(&str, "\"");
switch (opt->info.type) {
case DRI_INT:
case DRI_ENUM:
if (opt->info.range.start._int < opt->info.range.end._int) {
ralloc_asprintf_append(&str, " valid=\"%d:%d\"",
opt->info.range.start._int,
opt->info.range.end._int);
}
break;
case DRI_FLOAT:
if (opt->info.range.start._float < opt->info.range.end._float) {
ralloc_asprintf_append(&str, " valid=\"%f:%f\"",
opt->info.range.start._float,
opt->info.range.end._float);
}
break;
default:
break;
}
ralloc_asprintf_append(&str, ">\n"); /* end of <option> */
ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n",
opt->desc, opt->info.type != DRI_ENUM ? "/" : "");
if (opt->info.type == DRI_ENUM) {
for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) {
ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n",
opt->enums[i].value, opt->enums[i].desc);
}
ralloc_asprintf_append(&str, " </description>\n");
}
ralloc_asprintf_append(&str, " </option>\n");
}
assert(in_section);
ralloc_asprintf_append(&str, " </section>\n");
ralloc_asprintf_append(&str, "</driinfo>\n");
char *output = strdup(str);
ralloc_free(str);
return output;
}
#if WITH_XMLCONFIG
static bool
be_verbose(void)
{
@ -327,102 +71,6 @@ be_verbose(void)
return strstr(s, "silent") == NULL;
}
/**
* Print message to \c stderr if the \c LIBGL_DEBUG environment variable
* is set.
*
* Is called from the drivers.
*
* \param f \c printf like format string.
*/
static void
__driUtilMessage(const char *f, ...)
{
va_list args;
const char *libgl_debug;
libgl_debug=getenv("LIBGL_DEBUG");
if (libgl_debug && !strstr(libgl_debug, "quiet")) {
fprintf(stderr, "libGL: ");
va_start(args, f);
vfprintf(stderr, f, args);
va_end(args);
fprintf(stderr, "\n");
}
}
/** \brief Output a warning message. */
#define XML_WARNING1(msg) do { \
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser)); \
} while (0)
#define XML_WARNING(msg, ...) do { \
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser), \
##__VA_ARGS__); \
} while (0)
/** \brief Output an error message. */
#define XML_ERROR1(msg) do { \
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser)); \
} while (0)
#define XML_ERROR(msg, ...) do { \
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser), \
##__VA_ARGS__); \
} while (0)
/** \brief Parser context for configuration files. */
struct OptConfData {
const char *name;
XML_Parser parser;
driOptionCache *cache;
int screenNum;
const char *driverName, *execName;
const char *kernelDriverName;
const char *engineName;
const char *applicationName;
uint32_t engineVersion;
uint32_t applicationVersion;
uint32_t ignoringDevice;
uint32_t ignoringApp;
uint32_t inDriConf;
uint32_t inDevice;
uint32_t inApp;
uint32_t inOption;
};
/** \brief Elements in configuration files. */
enum OptConfElem {
OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
};
static const char *OptConfElems[] = {
[OC_APPLICATION] = "application",
[OC_DEVICE] = "device",
[OC_DRICONF] = "driconf",
[OC_ENGINE] = "engine",
[OC_OPTION] = "option",
};
static int compare(const void *a, const void *b) {
return strcmp(*(char *const*)a, *(char *const*)b);
}
/** \brief Binary search in a string array. */
static uint32_t
bsearchStr(const char *name, const char *elems[], uint32_t count)
{
const char **found;
found = bsearch(&name, elems, count, sizeof(char *), compare);
if (found)
return found - elems;
else
return count;
}
/** \brief Locale-independent integer parser.
*
* Works similar to strtol. Leading space is NOT skipped. The input
@ -602,6 +250,376 @@ parseValue(driOptionValue *v, driOptionType type, const char *string)
return true;
}
/** \brief Find an option in an option cache with the name as key */
static uint32_t
findOption(const driOptionCache *cache, const char *name)
{
uint32_t len = strlen(name);
uint32_t size = 1 << cache->tableSize, mask = size - 1;
uint32_t hash = 0;
uint32_t i, shift;
/* compute a hash from the variable length name */
for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
hash += (uint32_t)name[i] << shift;
hash *= hash;
hash = (hash >> (16-cache->tableSize/2)) & mask;
/* this is just the starting point of the linear search for the option */
for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
/* if we hit an empty entry then the option is not defined (yet) */
if (cache->info[hash].name == 0)
break;
else if (!strcmp(name, cache->info[hash].name))
break;
}
/* this assertion fails if the hash table is full */
assert (i < size);
return hash;
}
/** \brief Like strdup with error checking. */
#define XSTRDUP(dest,source) do { \
if (!(dest = strdup(source))) { \
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
abort(); \
} \
} while (0)
/** \brief Check if a value is in info->range. */
UNUSED static bool
checkValue(const driOptionValue *v, const driOptionInfo *info)
{
switch (info->type) {
case DRI_ENUM: /* enum is just a special integer */
case DRI_INT:
return (info->range.start._int == info->range.end._int ||
(v->_int >= info->range.start._int &&
v->_int <= info->range.end._int));
case DRI_FLOAT:
return (info->range.start._float == info->range.end._float ||
(v->_float >= info->range.start._float &&
v->_float <= info->range.end._float));
default:
return true;
}
}
void
driParseOptionInfo(driOptionCache *info,
const driOptionDescription *configOptions,
unsigned numOptions)
{
/* Make the hash table big enough to fit more than the maximum number of
* config options we've ever seen in a driver.
*/
info->tableSize = 6;
info->info = calloc(1 << info->tableSize, sizeof(driOptionInfo));
info->values = calloc(1 << info->tableSize, sizeof(driOptionValue));
if (info->info == NULL || info->values == NULL) {
fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
abort();
}
UNUSED bool in_section = false;
for (int o = 0; o < numOptions; o++) {
const driOptionDescription *opt = &configOptions[o];
if (opt->info.type == DRI_SECTION) {
in_section = true;
continue;
}
/* for driconf xml generation, options must always be preceded by a
* DRI_CONF_SECTION
*/
assert(in_section);
const char *name = opt->info.name;
int i = findOption(info, name);
driOptionInfo *optinfo = &info->info[i];
driOptionValue *optval = &info->values[i];
assert(!optinfo->name); /* No duplicate options in your list. */
optinfo->type = opt->info.type;
optinfo->range = opt->info.range;
XSTRDUP(optinfo->name, name);
switch (opt->info.type) {
case DRI_BOOL:
optval->_bool = opt->value._bool;
break;
case DRI_INT:
case DRI_ENUM:
optval->_int = opt->value._int;
break;
case DRI_FLOAT:
optval->_float = opt->value._float;
break;
case DRI_STRING:
XSTRDUP(optval->_string, opt->value._string);
break;
case DRI_SECTION:
unreachable("handled above");
}
/* Built-in default values should always be valid. */
assert(checkValue(optval, optinfo));
char *envVal = getenv(name);
if (envVal != NULL) {
driOptionValue v;
if (parseValue(&v, opt->info.type, envVal) &&
checkValue(&v, optinfo)) {
/* don't use XML_WARNING, we want the user to see this! */
if (be_verbose()) {
fprintf(stderr,
"ATTENTION: default value of option %s overridden by environment.\n",
name);
}
*optval = v;
} else {
fprintf(stderr, "illegal environment value for %s: \"%s\". Ignoring.\n",
name, envVal);
}
}
}
}
char *
driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions)
{
char *str = ralloc_strdup(NULL,
"<?xml version=\"1.0\" standalone=\"yes\"?>\n" \
"<!DOCTYPE driinfo [\n" \
" <!ELEMENT driinfo (section*)>\n" \
" <!ELEMENT section (description+, option+)>\n" \
" <!ELEMENT description (enum*)>\n" \
" <!ATTLIST description lang CDATA #FIXED \"en\"\n" \
" text CDATA #REQUIRED>\n" \
" <!ELEMENT option (description+)>\n" \
" <!ATTLIST option name CDATA #REQUIRED\n" \
" type (bool|enum|int|float) #REQUIRED\n" \
" default CDATA #REQUIRED\n" \
" valid CDATA #IMPLIED>\n" \
" <!ELEMENT enum EMPTY>\n" \
" <!ATTLIST enum value CDATA #REQUIRED\n" \
" text CDATA #REQUIRED>\n" \
"]>" \
"<driinfo>\n");
bool in_section = false;
for (int o = 0; o < numOptions; o++) {
const driOptionDescription *opt = &configOptions[o];
const char *name = opt->info.name;
const char *types[] = {
[DRI_BOOL] = "bool",
[DRI_INT] = "int",
[DRI_FLOAT] = "float",
[DRI_ENUM] = "enum",
[DRI_STRING] = "string",
};
if (opt->info.type == DRI_SECTION) {
if (in_section)
ralloc_asprintf_append(&str, " </section>\n");
ralloc_asprintf_append(&str,
" <section>\n"
" <description lang=\"en\" text=\"%s\"/>\n",
opt->desc);
in_section = true;
continue;
}
ralloc_asprintf_append(&str,
" <option name=\"%s\" type=\"%s\" default=\"",
name,
types[opt->info.type]);
switch (opt->info.type) {
case DRI_BOOL:
ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false");
break;
case DRI_INT:
case DRI_ENUM:
ralloc_asprintf_append(&str, "%d", opt->value._int);
break;
case DRI_FLOAT:
ralloc_asprintf_append(&str, "%f", opt->value._float);
break;
case DRI_STRING:
ralloc_asprintf_append(&str, "%s", opt->value._string);
break;
case DRI_SECTION:
unreachable("handled above");
break;
}
ralloc_asprintf_append(&str, "\"");
switch (opt->info.type) {
case DRI_INT:
case DRI_ENUM:
if (opt->info.range.start._int < opt->info.range.end._int) {
ralloc_asprintf_append(&str, " valid=\"%d:%d\"",
opt->info.range.start._int,
opt->info.range.end._int);
}
break;
case DRI_FLOAT:
if (opt->info.range.start._float < opt->info.range.end._float) {
ralloc_asprintf_append(&str, " valid=\"%f:%f\"",
opt->info.range.start._float,
opt->info.range.end._float);
}
break;
default:
break;
}
ralloc_asprintf_append(&str, ">\n"); /* end of <option> */
ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n",
opt->desc, opt->info.type != DRI_ENUM ? "/" : "");
if (opt->info.type == DRI_ENUM) {
for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) {
ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n",
opt->enums[i].value, opt->enums[i].desc);
}
ralloc_asprintf_append(&str, " </description>\n");
}
ralloc_asprintf_append(&str, " </option>\n");
}
assert(in_section);
ralloc_asprintf_append(&str, " </section>\n");
ralloc_asprintf_append(&str, "</driinfo>\n");
char *output = strdup(str);
ralloc_free(str);
return output;
}
#if WITH_XMLCONFIG
/**
* Print message to \c stderr if the \c LIBGL_DEBUG environment variable
* is set.
*
* Is called from the drivers.
*
* \param f \c printf like format string.
*/
static void
__driUtilMessage(const char *f, ...)
{
va_list args;
const char *libgl_debug;
libgl_debug=getenv("LIBGL_DEBUG");
if (libgl_debug && !strstr(libgl_debug, "quiet")) {
fprintf(stderr, "libGL: ");
va_start(args, f);
vfprintf(stderr, f, args);
va_end(args);
fprintf(stderr, "\n");
}
}
/** \brief Output a warning message. */
#define XML_WARNING1(msg) do { \
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser)); \
} while (0)
#define XML_WARNING(msg, ...) do { \
__driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser), \
##__VA_ARGS__); \
} while (0)
/** \brief Output an error message. */
#define XML_ERROR1(msg) do { \
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser)); \
} while (0)
#define XML_ERROR(msg, ...) do { \
__driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
(int) XML_GetCurrentLineNumber(data->parser), \
(int) XML_GetCurrentColumnNumber(data->parser), \
##__VA_ARGS__); \
} while (0)
/** \brief Parser context for configuration files. */
struct OptConfData {
const char *name;
XML_Parser parser;
driOptionCache *cache;
int screenNum;
const char *driverName, *execName;
const char *kernelDriverName;
const char *engineName;
const char *applicationName;
uint32_t engineVersion;
uint32_t applicationVersion;
uint32_t ignoringDevice;
uint32_t ignoringApp;
uint32_t inDriConf;
uint32_t inDevice;
uint32_t inApp;
uint32_t inOption;
};
/** \brief Elements in configuration files. */
enum OptConfElem {
OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
};
static const char *OptConfElems[] = {
[OC_APPLICATION] = "application",
[OC_DEVICE] = "device",
[OC_DRICONF] = "driconf",
[OC_ENGINE] = "engine",
[OC_OPTION] = "option",
};
static int compare(const void *a, const void *b) {
return strcmp(*(char *const*)a, *(char *const*)b);
}
/** \brief Binary search in a string array. */
static uint32_t
bsearchStr(const char *name, const char *elems[], uint32_t count)
{
const char **found;
found = bsearch(&name, elems, count, sizeof(char *), compare);
if (found)
return found - elems;
else
return count;
}
/** \brief Parse a list of ranges of type info->type. */
static unsigned char
parseRange(driOptionInfo *info, const char *string)