summaryrefslogtreecommitdiff
path: root/Tools/c-analyzer/c_globals
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_globals')
-rw-r--r--Tools/c-analyzer/c_globals/README72
-rw-r--r--Tools/c-analyzer/c_globals/__init__.py0
-rw-r--r--Tools/c-analyzer/c_globals/__main__.py209
-rw-r--r--Tools/c-analyzer/c_globals/find.py95
-rw-r--r--Tools/c-analyzer/c_globals/show.py16
-rw-r--r--Tools/c-analyzer/c_globals/supported.py393
6 files changed, 0 insertions, 785 deletions
diff --git a/Tools/c-analyzer/c_globals/README b/Tools/c-analyzer/c_globals/README
deleted file mode 100644
index 772b8be270..0000000000
--- a/Tools/c-analyzer/c_globals/README
+++ /dev/null
@@ -1,72 +0,0 @@
-#######################################
-# C Globals and CPython Runtime State.
-
-CPython's C code makes extensive use of global variables (whether static
-globals or static locals). Each such variable falls into one of several
-categories:
-
-* strictly const data
-* used exclusively in main or in the REPL
-* process-global state (e.g. managing process-level resources
- like signals and file descriptors)
-* Python "global" runtime state
-* per-interpreter runtime state
-
-The last one can be a problem as soon as anyone creates a second
-interpreter (AKA "subinterpreter") in a process. It is definitely a
-problem under subinterpreters if they are no longer sharing the GIL,
-since the GIL protects us from a lot of race conditions. Keep in mind
-that ultimately *all* objects (PyObject) should be treated as
-per-interpreter state. This includes "static types", freelists,
-_PyIdentifier, and singletons. Take that in for a second. It has
-significant implications on where we use static variables!
-
-Be aware that module-global state (stored in C statics) is a kind of
-per-interpreter state. There have been efforts across many years, and
-still going, to provide extension module authors mechanisms to store
-that state safely (see PEPs 3121, 489, etc.).
-
-(Note that there has been discussion around support for running multiple
-Python runtimes in the same process. That would ends up with the same
-problems, relative to static variables, that subinterpreters have.)
-
-Historically we have been bad at keeping per-interpreter state out of
-static variables, mostly because until recently subinterpreters were
-not widely used nor even factored in to solutions. However, the
-feature is growing in popularity and use in the community.
-
-Mandate: "Eliminate use of static variables for per-interpreter state."
-
-The "c-statics.py" script in this directory, along with its accompanying
-data files, are part of the effort to resolve existing problems with
-our use of static variables and to prevent future problems.
-
-#-------------------------
-## statics for actually-global state (and runtime state consolidation)
-
-In general, holding any kind of state in static variables
-increases maintenance burden and increases the complexity of code (e.g.
-we use TSS to identify the active thread state). So it is a good idea
-to avoid using statics for state even if for the "global" runtime or
-for process-global state.
-
-Relative to maintenance burden, one problem is where the runtime
-state is spread throughout the codebase in dozens of individual
-globals. Unlike the other globals, the runtime state represents a set
-of values that are constantly shifting in a complex way. When they are
-spread out it's harder to get a clear picture of what the runtime
-involves. Furthermore, when they are spread out it complicates efforts
-that change the runtime.
-
-Consequently, the globals for Python's runtime state have been
-consolidated under a single top-level _PyRuntime global. No new globals
-should be added for runtime state. Instead, they should be added to
-_PyRuntimeState or one of its sub-structs. The tools in this directory
-are run as part of the test suite to ensure that no new globals have
-been added. The script can be run manually as well:
-
- ./python Lib/test/test_c_statics/c-statics.py check
-
-If it reports any globals then they should be resolved. If the globals
-are runtime state then they should be folded into _PyRuntimeState.
-Otherwise they should be marked as ignored.
diff --git a/Tools/c-analyzer/c_globals/__init__.py b/Tools/c-analyzer/c_globals/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/Tools/c-analyzer/c_globals/__init__.py
+++ /dev/null
diff --git a/Tools/c-analyzer/c_globals/__main__.py b/Tools/c-analyzer/c_globals/__main__.py
deleted file mode 100644
index 9570fb6a14..0000000000
--- a/Tools/c-analyzer/c_globals/__main__.py
+++ /dev/null
@@ -1,209 +0,0 @@
-import argparse
-import os.path
-import re
-import sys
-
-from c_analyzer_common import SOURCE_DIRS, REPO_ROOT
-from c_analyzer_common.info import UNKNOWN
-from c_analyzer_common.known import (
- from_file as known_from_file,
- DATA_FILE as KNOWN_FILE,
- )
-from . import find, show
-from .supported import is_supported, ignored_from_file, IGNORED_FILE, _is_object
-
-
-def _match_unused_global(variable, knownvars, used):
- found = []
- for varid in knownvars:
- if varid in used:
- continue
- if varid.funcname is not None:
- continue
- if varid.name != variable.name:
- continue
- if variable.filename and variable.filename != UNKNOWN:
- if variable.filename == varid.filename:
- found.append(varid)
- else:
- found.append(varid)
- return found
-
-
-def _check_results(unknown, knownvars, used):
- badknown = set()
- for variable in sorted(unknown):
- msg = None
- if variable.funcname != UNKNOWN:
- msg = f'could not find global symbol {variable.id}'
- elif m := _match_unused_global(variable, knownvars, used):
- assert isinstance(m, list)
- badknown.update(m)
- elif variable.name in ('completed', 'id'): # XXX Figure out where these variables are.
- unknown.remove(variable)
- else:
- msg = f'could not find local symbol {variable.id}'
- if msg:
- #raise Exception(msg)
- print(msg)
- if badknown:
- print('---')
- print(f'{len(badknown)} globals in known.tsv, but may actually be local:')
- for varid in sorted(badknown):
- print(f'{varid.filename:30} {varid.name}')
- unused = sorted(varid
- for varid in set(knownvars) - used
- if varid.name != 'id') # XXX Figure out where these variables are.
- if unused:
- print('---')
- print(f'did not use {len(unused)} known vars:')
- for varid in unused:
- print(f'{varid.filename:30} {varid.funcname or "-":20} {varid.name}')
- raise Exception('not all known symbols used')
- if unknown:
- print('---')
- raise Exception('could not find all symbols')
-
-
-def _find_globals(dirnames, known, ignored):
- if dirnames == SOURCE_DIRS:
- dirnames = [os.path.relpath(d, REPO_ROOT) for d in dirnames]
-
- ignored = ignored_from_file(ignored)
- known = known_from_file(known)
-
- used = set()
- unknown = set()
- knownvars = (known or {}).get('variables')
- for variable in find.globals_from_binary(knownvars=knownvars,
- dirnames=dirnames):
- #for variable in find.globals(dirnames, known, kind='platform'):
- if variable.vartype == UNKNOWN:
- unknown.add(variable)
- continue
- yield variable, is_supported(variable, ignored, known)
- used.add(variable.id)
-
- #_check_results(unknown, knownvars, used)
-
-
-def cmd_check(cmd, dirs=SOURCE_DIRS, *,
- ignored=IGNORED_FILE,
- known=KNOWN_FILE,
- _find=_find_globals,
- _show=show.basic,
- _print=print,
- ):
- """
- Fail if there are unsupported globals variables.
-
- In the failure case, the list of unsupported variables
- will be printed out.
- """
- unsupported = [v for v, s in _find(dirs, known, ignored) if not s]
- if not unsupported:
- #_print('okay')
- return
-
- _print('ERROR: found unsupported global variables')
- _print()
- _show(sorted(unsupported))
- _print(f' ({len(unsupported)} total)')
- sys.exit(1)
-
-
-def cmd_show(cmd, dirs=SOURCE_DIRS, *,
- ignored=IGNORED_FILE,
- known=KNOWN_FILE,
- skip_objects=False,
- _find=_find_globals,
- _show=show.basic,
- _print=print,
- ):
- """
- Print out the list of found global variables.
-
- The variables will be distinguished as "supported" or "unsupported".
- """
- allsupported = []
- allunsupported = []
- for found, supported in _find(dirs, known, ignored):
- if skip_objects: # XXX Support proper filters instead.
- if _is_object(found.vartype):
- continue
- (allsupported if supported else allunsupported
- ).append(found)
-
- _print('supported:')
- _print('----------')
- _show(sorted(allsupported))
- _print(f' ({len(allsupported)} total)')
- _print()
- _print('unsupported:')
- _print('------------')
- _show(sorted(allunsupported))
- _print(f' ({len(allunsupported)} total)')
-
-
-#############################
-# the script
-
-COMMANDS = {
- 'check': cmd_check,
- 'show': cmd_show,
- }
-
-PROG = sys.argv[0]
-PROG = 'c-globals.py'
-
-
-def parse_args(prog=PROG, argv=sys.argv[1:], *, _fail=None):
- common = argparse.ArgumentParser(add_help=False)
- common.add_argument('--ignored', metavar='FILE',
- default=IGNORED_FILE,
- help='path to file that lists ignored vars')
- common.add_argument('--known', metavar='FILE',
- default=KNOWN_FILE,
- help='path to file that lists known types')
- common.add_argument('dirs', metavar='DIR', nargs='*',
- default=SOURCE_DIRS,
- help='a directory to check')
-
- parser = argparse.ArgumentParser(
- prog=prog,
- )
- subs = parser.add_subparsers(dest='cmd')
-
- check = subs.add_parser('check', parents=[common])
-
- show = subs.add_parser('show', parents=[common])
- show.add_argument('--skip-objects', action='store_true')
-
- if _fail is None:
- def _fail(msg):
- parser.error(msg)
-
- # Now parse the args.
- args = parser.parse_args(argv)
- ns = vars(args)
-
- cmd = ns.pop('cmd')
- if not cmd:
- _fail('missing command')
-
- return cmd, ns
-
-
-def main(cmd, cmdkwargs=None, *, _COMMANDS=COMMANDS):
- try:
- cmdfunc = _COMMANDS[cmd]
- except KeyError:
- raise ValueError(
- f'unsupported cmd {cmd!r}' if cmd else 'missing cmd')
-
- cmdfunc(cmd, **cmdkwargs or {})
-
-
-if __name__ == '__main__':
- cmd, cmdkwargs = parse_args()
- main(cmd, cmdkwargs)
diff --git a/Tools/c-analyzer/c_globals/find.py b/Tools/c-analyzer/c_globals/find.py
deleted file mode 100644
index a51b947cbd..0000000000
--- a/Tools/c-analyzer/c_globals/find.py
+++ /dev/null
@@ -1,95 +0,0 @@
-from c_analyzer_common import SOURCE_DIRS
-from c_analyzer_common.info import UNKNOWN
-from c_symbols import (
- info as s_info,
- binary as b_symbols,
- source as s_symbols,
- resolve,
- )
-from c_parser import info, declarations
-
-
-# XXX needs tests:
-# * iter_variables
-
-def globals_from_binary(binfile=b_symbols.PYTHON, *,
- knownvars=None,
- dirnames=None,
- _iter_symbols=b_symbols.iter_symbols,
- _resolve=resolve.symbols_to_variables,
- _get_symbol_resolver=resolve.get_resolver,
- ):
- """Yield a Variable for each found Symbol.
-
- Details are filled in from the given "known" variables and types.
- """
- symbols = _iter_symbols(binfile, find_local_symbol=None)
- #symbols = list(symbols)
- for variable in _resolve(symbols,
- resolve=_get_symbol_resolver(knownvars, dirnames),
- ):
- # Skip each non-global variable (unless we couldn't find it).
- # XXX Drop the "UNKNOWN" condition?
- if not variable.isglobal and variable.vartype != UNKNOWN:
- continue
- yield variable
-
-
-def globals_from_declarations(dirnames=SOURCE_DIRS, *,
- known=None,
- ):
- """Yield a Variable for each found declaration.
-
- Details are filled in from the given "known" variables and types.
- """
- raise NotImplementedError
-
-
-def iter_variables(kind='platform', *,
- known=None,
- dirnames=None,
- _resolve_symbols=resolve.symbols_to_variables,
- _get_symbol_resolver=resolve.get_resolver,
- _symbols_from_binary=b_symbols.iter_symbols,
- _symbols_from_source=s_symbols.iter_symbols,
- _iter_raw=declarations.iter_all,
- _iter_preprocessed=declarations.iter_preprocessed,
- ):
- """Yield a Variable for each one found (e.g. in files)."""
- kind = kind or 'platform'
-
- if kind == 'symbols':
- knownvars = (known or {}).get('variables')
- yield from _resolve_symbols(
- _symbols_from_source(dirnames, known),
- resolve=_get_symbol_resolver(knownvars, dirnames),
- )
- elif kind == 'platform':
- knownvars = (known or {}).get('variables')
- yield from _resolve_symbols(
- _symbols_from_binary(find_local_symbol=None),
- resolve=_get_symbol_resolver(knownvars, dirnames),
- )
- elif kind == 'declarations':
- for decl in _iter_raw(dirnames):
- if not isinstance(decl, info.Variable):
- continue
- yield decl
- elif kind == 'preprocessed':
- for decl in _iter_preprocessed(dirnames):
- if not isinstance(decl, info.Variable):
- continue
- yield decl
- else:
- raise ValueError(f'unsupported kind {kind!r}')
-
-
-def globals(dirnames, known, *,
- kind=None, # Use the default.
- _iter_variables=iter_variables,
- ):
- """Return a list of (StaticVar, <supported>) for each found global var."""
- for found in _iter_variables(kind, known=known, dirnames=dirnames):
- if not found.isglobal:
- continue
- yield found
diff --git a/Tools/c-analyzer/c_globals/show.py b/Tools/c-analyzer/c_globals/show.py
deleted file mode 100644
index f4298b17b6..0000000000
--- a/Tools/c-analyzer/c_globals/show.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-def basic(globals, *,
- _print=print):
- """Print each row simply."""
- for variable in globals:
- if variable.funcname:
- line = f'{variable.filename}:{variable.funcname}():{variable.name}'
- else:
- line = f'{variable.filename}:{variable.name}'
- vartype = variable.vartype
- #if vartype.startswith('static '):
- # vartype = vartype.partition(' ')[2]
- #else:
- # vartype = '=' + vartype
- line = f'{line:<64} {vartype}'
- _print(line)
diff --git a/Tools/c-analyzer/c_globals/supported.py b/Tools/c-analyzer/c_globals/supported.py
deleted file mode 100644
index d185daa246..0000000000
--- a/Tools/c-analyzer/c_globals/supported.py
+++ /dev/null
@@ -1,393 +0,0 @@
-import os.path
-import re
-
-from c_analyzer_common import DATA_DIR
-from c_analyzer_common.info import ID
-from c_analyzer_common.util import read_tsv, write_tsv
-
-
-IGNORED_FILE = os.path.join(DATA_DIR, 'ignored.tsv')
-
-IGNORED_COLUMNS = ('filename', 'funcname', 'name', 'kind', 'reason')
-IGNORED_HEADER = '\t'.join(IGNORED_COLUMNS)
-
-# XXX Move these to ignored.tsv.
-IGNORED = {
- # global
- 'PyImport_FrozenModules': 'process-global',
- 'M___hello__': 'process-global',
- 'inittab_copy': 'process-global',
- 'PyHash_Func': 'process-global',
- '_Py_HashSecret_Initialized': 'process-global',
- '_TARGET_LOCALES': 'process-global',
-
- # startup (only changed before/during)
- '_PyRuntime': 'runtime startup',
- 'runtime_initialized': 'runtime startup',
- 'static_arg_parsers': 'runtime startup',
- 'orig_argv': 'runtime startup',
- 'opt_ptr': 'runtime startup',
- '_preinit_warnoptions': 'runtime startup',
- '_Py_StandardStreamEncoding': 'runtime startup',
- 'Py_FileSystemDefaultEncoding': 'runtime startup',
- '_Py_StandardStreamErrors': 'runtime startup',
- 'Py_FileSystemDefaultEncodeErrors': 'runtime startup',
- 'Py_BytesWarningFlag': 'runtime startup',
- 'Py_DebugFlag': 'runtime startup',
- 'Py_DontWriteBytecodeFlag': 'runtime startup',
- 'Py_FrozenFlag': 'runtime startup',
- 'Py_HashRandomizationFlag': 'runtime startup',
- 'Py_IgnoreEnvironmentFlag': 'runtime startup',
- 'Py_InspectFlag': 'runtime startup',
- 'Py_InteractiveFlag': 'runtime startup',
- 'Py_IsolatedFlag': 'runtime startup',
- 'Py_NoSiteFlag': 'runtime startup',
- 'Py_NoUserSiteDirectory': 'runtime startup',
- 'Py_OptimizeFlag': 'runtime startup',
- 'Py_QuietFlag': 'runtime startup',
- 'Py_UTF8Mode': 'runtime startup',
- 'Py_UnbufferedStdioFlag': 'runtime startup',
- 'Py_VerboseFlag': 'runtime startup',
- '_Py_path_config': 'runtime startup',
- '_PyOS_optarg': 'runtime startup',
- '_PyOS_opterr': 'runtime startup',
- '_PyOS_optind': 'runtime startup',
- '_Py_HashSecret': 'runtime startup',
-
- # REPL
- '_PyOS_ReadlineLock': 'repl',
- '_PyOS_ReadlineTState': 'repl',
-
- # effectively const
- 'tracemalloc_empty_traceback': 'const',
- '_empty_bitmap_node': 'const',
- 'posix_constants_pathconf': 'const',
- 'posix_constants_confstr': 'const',
- 'posix_constants_sysconf': 'const',
- '_PySys_ImplCacheTag': 'const',
- '_PySys_ImplName': 'const',
- 'PyImport_Inittab': 'const',
- '_PyImport_DynLoadFiletab': 'const',
- '_PyParser_Grammar': 'const',
- 'Py_hexdigits': 'const',
- '_PyImport_Inittab': 'const',
- '_PyByteArray_empty_string': 'const',
- '_PyLong_DigitValue': 'const',
- '_Py_SwappedOp': 'const',
- 'PyStructSequence_UnnamedField': 'const',
-
- # signals are main-thread only
- 'faulthandler_handlers': 'signals are main-thread only',
- 'user_signals': 'signals are main-thread only',
- 'wakeup': 'signals are main-thread only',
-
- # hacks
- '_PySet_Dummy': 'only used as a placeholder',
- }
-
-BENIGN = 'races here are benign and unlikely'
-
-
-def is_supported(variable, ignored=None, known=None, *,
- _ignored=(lambda *a, **k: _is_ignored(*a, **k)),
- _vartype_okay=(lambda *a, **k: _is_vartype_okay(*a, **k)),
- ):
- """Return True if the given global variable is okay in CPython."""
- if _ignored(variable,
- ignored and ignored.get('variables')):
- return True
- elif _vartype_okay(variable.vartype,
- ignored.get('types')):
- return True
- else:
- return False
-
-
-def _is_ignored(variable, ignoredvars=None, *,
- _IGNORED=IGNORED,
- ):
- """Return the reason if the variable is a supported global.
-
- Return None if the variable is not a supported global.
- """
- if ignoredvars and (reason := ignoredvars.get(variable.id)):
- return reason
-
- if variable.funcname is None:
- if reason := _IGNORED.get(variable.name):
- return reason
-
- # compiler
- if variable.filename == 'Python/graminit.c':
- if variable.vartype.startswith('static state '):
- return 'compiler'
- if variable.filename == 'Python/symtable.c':
- if variable.vartype.startswith('static identifier '):
- return 'compiler'
- if variable.filename == 'Python/Python-ast.c':
- # These should be const.
- if variable.name.endswith('_field'):
- return 'compiler'
- if variable.name.endswith('_attribute'):
- return 'compiler'
-
- # other
- if variable.filename == 'Python/dtoa.c':
- # guarded by lock?
- if variable.name in ('p5s', 'freelist'):
- return 'dtoa is thread-safe?'
- if variable.name in ('private_mem', 'pmem_next'):
- return 'dtoa is thread-safe?'
- if variable.filename == 'Python/thread.c':
- # Threads do not become an issue until after these have been set
- # and these never get changed after that.
- if variable.name in ('initialized', 'thread_debug'):
- return 'thread-safe'
- if variable.filename == 'Python/getversion.c':
- if variable.name == 'version':
- # Races are benign here, as well as unlikely.
- return BENIGN
- if variable.filename == 'Python/fileutils.c':
- if variable.name == 'force_ascii':
- return BENIGN
- if variable.name == 'ioctl_works':
- return BENIGN
- if variable.name == '_Py_open_cloexec_works':
- return BENIGN
- if variable.filename == 'Python/codecs.c':
- if variable.name == 'ucnhash_CAPI':
- return BENIGN
- if variable.filename == 'Python/bootstrap_hash.c':
- if variable.name == 'getrandom_works':
- return BENIGN
- if variable.filename == 'Objects/unicodeobject.c':
- if variable.name == 'ucnhash_CAPI':
- return BENIGN
- if variable.name == 'bloom_linebreak':
- # *mostly* benign
- return BENIGN
- if variable.filename == 'Modules/getbuildinfo.c':
- if variable.name == 'buildinfo':
- # The static is used for pre-allocation.
- return BENIGN
- if variable.filename == 'Modules/posixmodule.c':
- if variable.name == 'ticks_per_second':
- return BENIGN
- if variable.name == 'dup3_works':
- return BENIGN
- if variable.filename == 'Modules/timemodule.c':
- if variable.name == 'ticks_per_second':
- return BENIGN
- if variable.filename == 'Objects/longobject.c':
- if variable.name == 'log_base_BASE':
- return BENIGN
- if variable.name == 'convwidth_base':
- return BENIGN
- if variable.name == 'convmultmax_base':
- return BENIGN
-
- return None
-
-
-def _is_vartype_okay(vartype, ignoredtypes=None):
- if _is_object(vartype):
- return None
-
- if vartype.startswith('static const '):
- return 'const'
- if vartype.startswith('const '):
- return 'const'
-
- # components for TypeObject definitions
- for name in ('PyMethodDef', 'PyGetSetDef', 'PyMemberDef'):
- if name in vartype:
- return 'const'
- for name in ('PyNumberMethods', 'PySequenceMethods', 'PyMappingMethods',
- 'PyBufferProcs', 'PyAsyncMethods'):
- if name in vartype:
- return 'const'
- for name in ('slotdef', 'newfunc'):
- if name in vartype:
- return 'const'
-
- # structseq
- for name in ('PyStructSequence_Desc', 'PyStructSequence_Field'):
- if name in vartype:
- return 'const'
-
- # other definiitions
- if 'PyModuleDef' in vartype:
- return 'const'
-
- # thread-safe
- if '_Py_atomic_int' in vartype:
- return 'thread-safe'
- if 'pthread_condattr_t' in vartype:
- return 'thread-safe'
-
- # startup
- if '_Py_PreInitEntry' in vartype:
- return 'startup'
-
- # global
-# if 'PyMemAllocatorEx' in vartype:
-# return True
-
- # others
-# if 'PyThread_type_lock' in vartype:
-# return True
-
- # XXX ???
- # _Py_tss_t
- # _Py_hashtable_t
- # stack_t
- # _PyUnicode_Name_CAPI
-
- # functions
- if '(' in vartype and '[' not in vartype:
- return 'function pointer'
-
- # XXX finish!
- # * allow const values?
- #raise NotImplementedError
- return None
-
-
-PYOBJECT_RE = re.compile(r'''
- ^
- (
- # must start with "static "
- static \s+
- (
- identifier
- )
- \b
- ) |
- (
- # may start with "static "
- ( static \s+ )?
- (
- .*
- (
- PyObject |
- PyTypeObject |
- _? Py \w+ Object |
- _PyArg_Parser |
- _Py_Identifier |
- traceback_t |
- PyAsyncGenASend |
- _PyAsyncGenWrappedValue |
- PyContext |
- method_cache_entry
- )
- \b
- ) |
- (
- (
- _Py_IDENTIFIER |
- _Py_static_string
- )
- [(]
- )
- )
- ''', re.VERBOSE)
-
-
-def _is_object(vartype):
- if 'PyDictKeysObject' in vartype:
- return False
- if PYOBJECT_RE.match(vartype):
- return True
- if vartype.endswith((' _Py_FalseStruct', ' _Py_TrueStruct')):
- return True
-
- # XXX Add more?
-
- #for part in vartype.split():
- # # XXX const is automatic True?
- # if part == 'PyObject' or part.startswith('PyObject['):
- # return True
- return False
-
-
-def ignored_from_file(infile, *,
- _read_tsv=read_tsv,
- ):
- """Yield a Variable for each ignored var in the file."""
- ignored = {
- 'variables': {},
- #'types': {},
- #'constants': {},
- #'macros': {},
- }
- for row in _read_tsv(infile, IGNORED_HEADER):
- filename, funcname, name, kind, reason = row
- if not funcname or funcname == '-':
- funcname = None
- id = ID(filename, funcname, name)
- if kind == 'variable':
- values = ignored['variables']
- else:
- raise ValueError(f'unsupported kind in row {row}')
- values[id] = reason
- return ignored
-
-
-##################################
-# generate
-
-def _get_row(varid, reason):
- return (
- varid.filename,
- varid.funcname or '-',
- varid.name,
- 'variable',
- str(reason),
- )
-
-
-def _get_rows(variables, ignored=None, *,
- _as_row=_get_row,
- _is_ignored=_is_ignored,
- _vartype_okay=_is_vartype_okay,
- ):
- count = 0
- for variable in variables:
- reason = _is_ignored(variable,
- ignored and ignored.get('variables'),
- )
- if not reason:
- reason = _vartype_okay(variable.vartype,
- ignored and ignored.get('types'))
- if not reason:
- continue
-
- print(' ', variable, repr(reason))
- yield _as_row(variable.id, reason)
- count += 1
- print(f'total: {count}')
-
-
-def _generate_ignored_file(variables, filename=None, *,
- _generate_rows=_get_rows,
- _write_tsv=write_tsv,
- ):
- if not filename:
- filename = IGNORED_FILE + '.new'
- rows = _generate_rows(variables)
- _write_tsv(filename, IGNORED_HEADER, rows)
-
-
-if __name__ == '__main__':
- from c_analyzer_common import SOURCE_DIRS
- from c_analyzer_common.known import (
- from_file as known_from_file,
- DATA_FILE as KNOWN_FILE,
- )
- from . import find
- known = known_from_file(KNOWN_FILE)
- knownvars = (known or {}).get('variables')
- variables = find.globals_from_binary(knownvars=knownvars,
- dirnames=SOURCE_DIRS)
-
- _generate_ignored_file(variables)