The initial implementation of a diagnostic tool that collects valuable
information about esp-idf and failed build to assist in investigating
reported issues.
The gathered information includes environmental variables, details about
the python virtual environment, installed tools, platform information,
project_description.json, sdkconfig, build logs, map file, linker
scripts, and others.
usage:
1) create the default report
# allow diag to create the report directory name
$ idf.py diag
# explicitly specify the report directory
$ idf.py diag --output <report directory>
2) examine the contents of the generated <report directory> for
sensitive information and add additional content to the
<report directory>
3) create report archive zip file that can be shared or attached to
the reported issue
$ idf.py diag --zip <report directory>
The tool collects information as described in what are known as recipe
files. A recipe file is a YAML file, similar to an Ansible playbook or a
GitHub action, but much more simplified. Each recipe outlines how to
gather a set of related information. For instance, the manager.yml
recipe gathers data related to the component manager. Each recipe
includes metadata such as its description, tags, and steps. Tags are
used to determine which recipes to use; by default, all built-in recipes
located in tools/idf_py_actions/diag/recipes are used. Steps consist of
a list of commands to be executed. Currently, there are four commands:
file, exec, env, and glob. For more detailed information about recipes,
their format, and commands, please refer to
tools/idf_py_actions/diag/recipes/README.md.
Recipe example for component manager:
description: IDF Component Manager information
tags: [manager, base, project]
output: manager
steps:
- name: 'IDF Component Manager'
cmds:
- exec:
cmd: 'python -m idf_component_manager version'
output: manager.ver
- file:
path: '${PROJECT_DIR}/dependencies.lock'
- glob:
# Gather all idf_component.yml files from the project directory and
# save them in directories relative to the project directory within
# the idf_component directory.
pattern: 'idf_component.yml'
recursive: true
relative: true
path: '${PROJECT_DIR}'
output: 'idf_component/'
Create report for manager
1) all recipes with manager tag
$ idf.py diag --tag manager
2) use only the manager recipe explicitly; built-in recipes can be
referenced simply by their name, but all recipes can be referenced
by their path
$ idf.py diag --recipe manager
or
$ idf.py diag --recipe <full path>
To display available recipes, use
$ idf.py diag --list
and to verify recipes, use
$ idf.py diag --check
Both --list and --check honers the --tag and --recipe options.
Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
Recipe format description for idf.py diag
The idf.py diag command processes one or more recipe files. Each recipe
file outlines a collection of related data and files that should be gathered.
For instance, the idf.yml recipe gathers information related to the ESP-IDF.
Recipes are formatted in YAML. The output from each recipe consists of a
set of files located in the diagnostic report directory, which may be copied or
generated as a result of executing certain commands.
A recipe is made up of one or more steps, and each step contains one or
more commands. A recipe can consist of just one step where all the commands
are carried out. The aim is to split the recipe into logical steps if it is
convenient. For instance, a project.yml recipe might include one step to
gather all log files and another to collect linker script files, although these
tasks could also be completed in a single step. Refer to the recipes in
this directory for examples.
Overview of Recipe Structure
description: brief recipe description
tags: list of tags that can be used to identify the recipe
output: root recipe directory for its output in the report folder
steps: list of steps to execute within a recipe
- name: brief step description
output: step directory for its output in the report folder
cmds: list of commands to execute within a step
- cmd: command name
arg: command argument
...
...
...
Recipe variables
The recipe can utilize the following variables. The idf.py diag assigns
values to these variables and expands them in the recipe upon loading. To use a
variable, format it as ${NAME}, such as ${IDF_PATH}.
-
PROJECT_DIR
Project directory specified by idf.py using the
-Cor--project-diroption. -
BUILD_DIR
Build directory specified by idf.py using the
-Bor--build-diroption. -
IDF_PATH
IDF path as defined in the environment variable.
-
REPORT_DIR
The report directory is where all the recipe outputs are stored. Keep in mind that during the execution of
idf.py diag, it points to a temporary directory, which is moved to its final destination once the report is successfully completed.
Recipe
-
description: string (required)
Short
recipedescription, which is shown inidf.py diagprogress. -
tags: list (optional)
Strings which identify this
recipe. Used to specify whichrecipesshould theidf.py diaguse. Allrecipeswith a given tag are used. -
output: string (optional)
Global output directory for the
recipe. This directory serves as the main directory for all files produced by thisrecipewithin the report directory. For instance, if it is set toidfand the report directory isreport, then all files collected by thisrecipewill be stored in thereport/idfdirectory. This helps organize the collected files within the report directory. -
steps: list (required)
One or more
stepsto follow for thisrecipe. This allows, but does not require, splitting therecipeinto more logical sections. For example, onestepcould gather all log files, while another might collect environment information.
Step
-
name: string (required)
Brief description of the
step, displayed in theidf.py diagprogress beneath therecipedescription. -
output: string (optional)
Global output directory for the
step. This directory serves as the main directory for all files produced by thisstepwithin the recipeoutputdirectory. For instance, if it is set tologsand the report directory isreportand recipeoutputisidf, then all files collected by thisstepwill be stored in thereport/idf/logsdirectory. -
cmds: list (required)
Sequence of commands to be executed in this
step.
Commands
The following commands can be used within a step in the recipe. Each
command consists of a list that includes the command name, such as exec or
glob, along with its arguments. Please be aware that if a command fails, it
does not terminate idf.py diag; all commands will still be executed. The
command mapping key has no value, and if it is present, it is ignored.
file
Copy the specified file from the given path to the report directory. If no
output argument is provided, the file is copied using its original name.
-
path: string (required)
Path to the source file.
-
output: string (optional)
The destination path for the file within the report directory. If it ends with a
/character, it is treated as a directory, and the source file name from thepathargument is added to it. Otherwise, it is considered a complete path including the file name. If not provided, the source file name is used as the destination file name. The complete destination path in the report directory is built starting with theoutputdirectory specified in therecipe, followed by theoutputdirectory specified in thestep, and finally theoutputspecified in thefilecommand, if each is provided. All directories that do not exist in the output path are created.
Example:
- file:
path: '${BUILD_DIR}/compile_commands.json'
output: 'build/json_files/commands.json'
exec
Run the given command and save its output.
-
cmd: string or list (required)
Command to run. If it's a string, it starts via the shell; if it's a list, it starts without shell expansions. Using the list format can help avoid escaping command arguments.
-
output: string (optional)
The path in the report directory where the command's standard output should be stored. If not specified, the command output will not be saved. The complete destination path in the report directory is built starting with the
outputdirectory specified in therecipe, followed by theoutputdirectory specified in thestep, and finally theoutputspecified in theexeccommand, if each is provided. The/character at the end is disregarded in contrast to thefilecommand. -
stderr: string (optional)
Similar to
output, but specifically for standard error output. -
timeout: int (optional)
The number of seconds to wait for the command to execute.
-
append: boolean (optional)
Append the command's standard output and standard error to the files specified by the
outputandstderrarguments.
Example:
- exec:
cmd: 'idf.py --version'
timeout: 10
output: esp_idf.ver
- exec:
cmd:
- python
- -c
- |
import platform
print(f'system: {platform.system()}')
env
Store details of the specified environment variables in a file using the format variable=value.
-
vars: list (required)
A list of names for environment variables to gather.
-
regex: string (optional)
Optional regular expression to gather environment variables that match it.
-
output: string (optional)
The path in the report directory where the environment variable information should be stored. If not specified, nothing will be saved. The complete destination path in the report directory is built starting with the
outputdirectory specified in therecipe, followed by theoutputdirectory specified in thestep, and finally theoutputspecified in theenvcommand, if each is provided. The/character at the end is disregarded, in contrast to thefilecommand. -
append: boolean (optional)
Append the environmental variables information to the file specified by the
outputargument.
Example:
- env:
vars:
- IDF_PATH
- IDF_PYTHON_ENV_PATH
- IDF_TOOLS_PATH
- PATH
- OPENOCD_SCRIPTS
- PYTHONPATH
- MSYSTEM
regex: '.*IDF.*|.*ESP.*'
output: environment.var
glob
Find pathnames that match a specific pattern within a specified directory, and include them in the report directory.
-
pattern: string (required)
Pattern matching with wildcards.
-
path: string (required)
Directory to look for files that match the
pattern. -
output: string (optional)
Every file that matches the
patternis stored in the report directory at the location specified byoutput. Ifoutputends with a/, it is interpreted as a directory, and the matched file name is appended to it. If it does not end with a/, it is regarded as a full path including the file name. Ifoutputis not specified, the name of the matching file is used as the destination file name.The full destination path in the report directory for each matched file is constructed by using the
outputdirectory from therecipe, followed by theoutputdirectory from thestep, and finally theoutputfrom theglobcommand, if each is available. Any directories that do not exist in the output path are created. If therelativeargument is set and theoutputargument in theglobcommand ends with/, the file's relative path to thepathargument is appended to theoutputargument in theglobcommand. A.<cnt>suffix is added to a file path if that path already exists in the report directory. The<cnt>is a number that increases until a unique path is found in the report directory. -
mtime: boolean (optional)
If multiple files are found, add only the one that was modified most recently.
-
recursive: boolean (optional)
Recursively search the
path. -
relative: boolean (optional)
Save the file in the
outputdirectory, preserving the same relative path it has in thepathdirectory. -
regex: string (optional)
Only add files whose content matches the specified regular expression.
Example:
- glob:
pattern: 'idf_py_stdout_output*'
path: '${BUILD_DIR}/log'
regex: '^Command: .*idf_monitor.py.*$'
output: 'logs/monitor/'
mtime: True