mirror of https://github.com/acidanthera/audk.git
254 lines
12 KiB
Markdown
254 lines
12 KiB
Markdown
# Debug Macro Check
|
|
|
|
This Python application scans all files in a build package for debug macro formatting issues. It is intended to be a
|
|
fundamental build-time check that is part of a normal developer build process to catch errors right away.
|
|
|
|
As a build plugin, it is capable of finding these errors early in the development process after code is initially
|
|
written to ensure that all code tested is free of debug macro formatting errors. These errors often creep into debug
|
|
prints in error conditions that are not frequently executed making debug even more difficult and confusing when they
|
|
are encountered. In other cases, debug macros with these errors in the main code path can lead to unexpected behavior
|
|
when executed. As a standalone script, it can be easily run manually or integrated into other CI processes.
|
|
|
|
The plugin is part of a set of debug macro check scripts meant to be relatively portable so they can be applied to
|
|
additional code bases with minimal effort.
|
|
|
|
## 1. BuildPlugin/DebugMacroCheckBuildPlugin.py
|
|
|
|
This is the build plugin. It is discovered within the Stuart Self-Describing Environment (SDE) due to the accompanying
|
|
file `DebugMacroCheck_plugin_in.yaml`.
|
|
|
|
Since macro errors are considered a coding bug that should be found and fixed during the build phase of the developer
|
|
process (before debug and testing), this plugin is run in pre-build. It will run within the scope of the package
|
|
being compiled. For a platform build, this means it will run against the package being built. In a CI build, it will
|
|
run in pre-build for each package as each package is built.
|
|
|
|
The build plugin has the following attributes:
|
|
|
|
1. Registered at `global` scope. This means it will always run.
|
|
|
|
2. Called only on compilable build targets (i.e. does nothing on `"NO-TARGET"`).
|
|
|
|
3. Runs as a pre-build step. This means it gives results right away to ensure compilation follows on a clean slate.
|
|
This also means it runs in platform build and CI. It is run in CI as a pre-build step when the `CompilerPlugin`
|
|
compiles code. This ensures even if the plugin was not run locally, all code submissions have been checked.
|
|
|
|
4. Reports any errors in the build log and fails the build upon error making it easy to discover problems.
|
|
|
|
5. Supports two methods of configuration via "substitution strings":
|
|
|
|
1. By setting a build variable called `DEBUG_MACRO_CHECK_SUB_FILE` with the name of a substitution YAML file to
|
|
use.
|
|
|
|
**Example:**
|
|
|
|
```python
|
|
shell_environment.GetBuildVars().SetValue(
|
|
"DEBUG_MACRO_CHECK_SUB_FILE",
|
|
os.path.join(self.GetWorkspaceRoot(), "DebugMacroCheckSub.yaml"),
|
|
"Set in CISettings.py")
|
|
```
|
|
|
|
**Substitution File Content Example:**
|
|
|
|
```yaml
|
|
---
|
|
# OvmfPkg/CpuHotplugSmm/ApicId.h
|
|
# Reason: Substitute with macro value
|
|
FMT_APIC_ID: 0x%08x
|
|
|
|
# DynamicTablesPkg/Include/ConfigurationManagerObject.h
|
|
# Reason: Substitute with macro value
|
|
FMT_CM_OBJECT_ID: 0x%lx
|
|
|
|
# OvmfPkg/IntelTdx/TdTcg2Dxe/TdTcg2Dxe.c
|
|
# Reason: Acknowledging use of two format specifiers in string with one argument
|
|
# Replace ternary operator in debug string with single specifier
|
|
'Index == COLUME_SIZE/2 ? " | %02x" : " %02x"': "%d"
|
|
|
|
# DynamicTablesPkg/Library/Common/TableHelperLib/ConfigurationManagerObjectParser.c
|
|
# ShellPkg/Library/UefiShellAcpiViewCommandLib/AcpiParser.c
|
|
# Reason: Acknowledge that string *should* expand to one specifier
|
|
# Replace variable with expected number of specifiers (1)
|
|
Parser[Index].Format: "%d"
|
|
```
|
|
|
|
2. By entering the string substitutions directory into a dictionary called `StringSubstitutions` in a
|
|
`DebugMacroCheck` section of the package CI YAML file.
|
|
|
|
**Example:**
|
|
|
|
```yaml
|
|
"DebugMacroCheck": {
|
|
"StringSubstitutions": {
|
|
"SUB_A": "%Lx"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Debug Macro Check Build Plugin: Simple Disable
|
|
|
|
The build plugin can simply be disabled by setting an environment variable named `"DISABLE_DEBUG_MACRO_CHECK"`. The
|
|
plugin is disabled on existence of the variable. The contents of the variable are not inspected at this time.
|
|
|
|
## 2. DebugMacroCheck.py
|
|
|
|
This is the main Python module containing the implementation logic. The build plugin simply wraps around it.
|
|
|
|
When first running debug macro check against a new, large code base, it is recommended to first run this standalone
|
|
script and address all of the issues and then enable the build plugin.
|
|
|
|
The module supports a number of configuration parameters to ease debug of errors and to provide flexibility for
|
|
different build environments.
|
|
|
|
### EDK 2 PyTool Library Dependency
|
|
|
|
This script has minimal library dependencies. However, it has one dependency you might not be familiar with on the
|
|
Tianocore EDK 2 PyTool Library (edk2toollib):
|
|
|
|
```py
|
|
from edk2toollib.utility_functions import RunCmd
|
|
```
|
|
|
|
You simply need to install the following pip module to use this library: `edk2-pytool-library`
|
|
(e.g. `pip install edk2-pytool-library`)
|
|
|
|
More information is available here:
|
|
|
|
- PyPI page: [edk2-pytool-library](https://pypi.org/project/edk2-pytool-library/)
|
|
- GitHub repo: [tianocore/edk2-pytool-library](https://github.com/tianocore/edk2-pytool-library)
|
|
|
|
If you strongly prefer not including this additional dependency, the functionality imported here is relatively
|
|
simple to substitute with the Python [`subprocess`](https://docs.python.org/3/library/subprocess.html) built-in
|
|
module.
|
|
|
|
### Examples
|
|
|
|
Simple run against current directory:
|
|
|
|
`> python DebugMacroCheck.py -w .`
|
|
|
|
Simple run against a single file:
|
|
|
|
`> python DebugMacroCheck.py -i filename.c`
|
|
|
|
Run against a directory with output placed into a file called "debug_macro_check.log":
|
|
|
|
`> python DebugMacroCheck.py -w . -l`
|
|
|
|
Run against a directory with output placed into a file called "custom.log" and debug log messages enabled:
|
|
|
|
`> python DebugMacroCheck.py -w . -l custom.log -v`
|
|
|
|
Run against a directory with output placed into a file called "custom.log", with debug log messages enabled including
|
|
python script function and line number, use a substitution file called "file_sub.yaml", do not show the progress bar,
|
|
and run against .c and .h files:
|
|
|
|
`> python DebugMacroCheck.py -w . -l custom.log -vv -s file_sub.yaml -n -e .c .h`
|
|
|
|
> **Note**: It is normally not recommended to run against .h files as they and many other non-.c files normally do
|
|
not have full `DEBUG` macro prints.
|
|
|
|
```plaintext
|
|
usage: Debug Macro Checker [-h] (-w WORKSPACE_DIRECTORY | -i [INPUT_FILE]) [-l [LOG_FILE]] [-s SUBSTITUTION_FILE] [-v] [-n] [-q] [-u]
|
|
[-df] [-ds] [-e [EXTENSIONS ...]]
|
|
|
|
Checks for debug macro formatting errors within files recursively located within a given directory.
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
-w WORKSPACE_DIRECTORY, --workspace-directory WORKSPACE_DIRECTORY
|
|
Directory of source files to check.
|
|
|
|
-i [INPUT_FILE], --input-file [INPUT_FILE]
|
|
File path for an input file to check.
|
|
|
|
Note that some other options do not apply if a single file is specified such as the
|
|
git options and file extensions.
|
|
|
|
-e [EXTENSIONS ...], --extensions [EXTENSIONS ...]
|
|
List of file extensions to include.
|
|
(default: ['.c'])
|
|
|
|
Optional input and output:
|
|
-l [LOG_FILE], --log-file [LOG_FILE]
|
|
File path for log output.
|
|
(default: if the flag is given with no file path then a file called
|
|
debug_macro_check.log is created and used in the current directory)
|
|
|
|
-s SUBSTITUTION_FILE, --substitution-file SUBSTITUTION_FILE
|
|
A substitution YAML file specifies string substitutions to perform within the debug macro.
|
|
|
|
This is intended to be a simple mechanism to expand the rare cases of pre-processor
|
|
macros without directly involving the pre-processor. The file consists of one or more
|
|
string value pairs where the key is the identifier to replace and the value is the value
|
|
to replace it with.
|
|
|
|
This can also be used as a method to ignore results by replacing the problematic string
|
|
with a different string.
|
|
|
|
-v, --verbose-log-file
|
|
Set file logging verbosity level.
|
|
- None: Info & > level messages
|
|
- '-v': + Debug level messages
|
|
- '-vv': + File name and function
|
|
- '-vvv': + Line number
|
|
- '-vvvv': + Timestamp
|
|
(default: verbose logging is not enabled)
|
|
|
|
-n, --no-progress-bar
|
|
Disables progress bars.
|
|
(default: progress bars are used in some places to show progress)
|
|
|
|
-q, --quiet Disables console output.
|
|
(default: console output is enabled)
|
|
|
|
-u, --utf8w Shows warnings for file UTF-8 decode errors.
|
|
(default: UTF-8 decode errors are not shown)
|
|
|
|
|
|
Optional git control:
|
|
-df, --do-not-ignore-git-ignore-files
|
|
Do not ignore git ignored files.
|
|
(default: files in git ignore files are ignored)
|
|
|
|
-ds, --do-not-ignore-git_submodules
|
|
Do not ignore files in git submodules.
|
|
(default: files in git submodules are ignored)
|
|
```
|
|
|
|
## String Substitutions
|
|
|
|
`DebugMacroCheck` currently runs separate from the compiler toolchain. This has the advantage that it is very portable
|
|
and can run early in the build process, but it also means pre-processor macro expansion does not happen when it is
|
|
invoked.
|
|
|
|
In practice, it has been very rare that this is an issue for how most debug macros are written. In case it is, a
|
|
substitution file can be used to inform `DebugMacroCheck` about the string substitution the pre-processor would
|
|
perform.
|
|
|
|
This pattern should be taken as a warning. It is just as difficult for humans to keep debug macro specifiers and
|
|
arguments balanced as it is for `DebugMacroCheck` pre-processor macro substitution is used. By separating the string
|
|
from the actual arguments provided, it is more likely for developers to make mistakes matching print specifiers in
|
|
the string to the arguments. If usage is reasonable, a string substitution can be used as needed.
|
|
|
|
### Ignoring Errors
|
|
|
|
Since substitution files perform a straight textual substitution in macros discovered, it can be used to replace
|
|
problematic text with text that passes allowing errors to be ignored.
|
|
|
|
## Python Version Required (3.10)
|
|
|
|
This script is written to take advantage of new Python language features in Python 3.10. If you are not using Python
|
|
3.10 or later, you can:
|
|
|
|
1. Upgrade to Python 3.10 or greater
|
|
2. Run this script in a [virtual environment](https://docs.python.org/3/tutorial/venv.html) with Python 3.10
|
|
or greater
|
|
3. Customize the script for compatibility with your Python version
|
|
|
|
These are listed in order of recommendation. **(1)** is the simplest option and will upgrade your environment to a
|
|
newer, safer, and better Python experience. **(2)** is the simplest approach to isolate dependencies to what is needed
|
|
to run this script without impacting the rest of your system environment. **(3)** creates a one-off fork of the script
|
|
that, by nature, has a limited lifespan and will make accepting future updates difficult but can be done with relatively
|
|
minimal effort back to recent Python 3 releases.
|