mirror of https://github.com/pwndbg/pwndbg.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.3 KiB
Python
218 lines
7.3 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import textwrap
|
|
from typing import Dict
|
|
from typing import Tuple
|
|
|
|
from mdutils.mdutils import MdUtils
|
|
|
|
from scripts._docs.configuration_docs_common import BASE_PATH
|
|
from scripts._docs.configuration_docs_common import ExtractedParam
|
|
from scripts._docs.configuration_docs_common import extracted_filename
|
|
from scripts._docs.gen_docs_generic import ALL_DEBUGGERS
|
|
from scripts._docs.gen_docs_generic import strip_ansi_color
|
|
from scripts._docs.gen_docs_generic import update_files_simple
|
|
from scripts._docs.gen_docs_generic import verify_existence
|
|
from scripts._docs.gen_docs_generic import verify_files_simple
|
|
|
|
|
|
def convert_to_markdown(scope: str, debugger_to_params: Dict[str, list[ExtractedParam]]) -> str:
|
|
"""
|
|
Returns:
|
|
The contents of the file corresponding to the passed scope.
|
|
"""
|
|
# filename doesn't matter since we only use mdFile.get_md_text()
|
|
mdFile = MdUtils(scope)
|
|
mdFile.new_header(level=1, title=scope)
|
|
|
|
all_params: set[str] = set()
|
|
for _, params in debugger_to_params.items():
|
|
all_params.update([param.name for param in params])
|
|
|
|
for param_name in sorted(all_params):
|
|
# Make a (debugger name, parameter) list in case some
|
|
# debuggers disagree on what some parameter should
|
|
# display. We won't add debuggers that don't have the
|
|
# parameter.
|
|
param_variants: list[Tuple[str, ExtractedParam]] = []
|
|
|
|
for debugger, dparams in debugger_to_params.items():
|
|
# Slow but whatever
|
|
for dparam in dparams:
|
|
if param_name == dparam.name:
|
|
param_variants.append((debugger, dparam))
|
|
break
|
|
|
|
assert param_variants
|
|
|
|
mdFile.new_header(level=2, title="**" + param_name + "**")
|
|
|
|
# Note about supported debuggers if the parameter isn't
|
|
# available everywhere.
|
|
if len(param_variants) != len(ALL_DEBUGGERS):
|
|
supported_list = ", ".join([x[0].upper() for x in param_variants])
|
|
md = '<small style="color: lightgray;">'
|
|
md += f"(only in {supported_list})"
|
|
md += "</small>\n"
|
|
mdFile.write(md)
|
|
|
|
debuggers_agree = all(x[1] == param_variants[0][1] for x in param_variants)
|
|
|
|
if debuggers_agree:
|
|
mdFile.new_paragraph(param_variants[0][1].set_show_doc)
|
|
mdFile.new_paragraph(param_variants[0][1].help_docstring)
|
|
else:
|
|
for debugger, dparam in sorted(param_variants):
|
|
# Content tabs
|
|
# https://squidfunk.github.io/mkdocs-material/reference/content-tabs/
|
|
mdFile.write(f'\n=== "{debugger.upper()}"')
|
|
|
|
indented_set_show_doc = textwrap.indent(dparam.set_show_doc, " ")
|
|
mdFile.new_paragraph(indented_set_show_doc)
|
|
|
|
indented_help_docstring = textwrap.indent(dparam.help_docstring, " ")
|
|
mdFile.new_paragraph(indented_help_docstring)
|
|
|
|
mdFile.write("\n\n----------\n")
|
|
|
|
autogen_warning = (
|
|
"<!-- THIS WHOLE FILE IS AUTOGENERATED. DO NOT MODIFY IT. See scripts/generate-docs.sh -->"
|
|
)
|
|
return autogen_warning + "\n" + strip_ansi_color(mdFile.get_md_text())
|
|
|
|
|
|
def check_index(num_scopes: int):
|
|
assert (
|
|
num_scopes == 3
|
|
and "It seems a new scope has been added, "
|
|
f"please update the index file ({INDEX_PATH}) and bump this number accordingly."
|
|
)
|
|
|
|
|
|
def convert_all_to_markdown(
|
|
extracted: list[Tuple[str, Dict[str, list[ExtractedParam]]]],
|
|
) -> Dict[str, str]:
|
|
result = {}
|
|
|
|
# Enumerate all scopes we can see.
|
|
all_scopes: set[str] = set()
|
|
for _, data in extracted:
|
|
for scope in data.keys():
|
|
all_scopes.add(scope)
|
|
|
|
# Check if the index is up to date while
|
|
# we are here.
|
|
check_index(len(all_scopes))
|
|
|
|
# Generate markdown for those scopes.
|
|
for scope in all_scopes:
|
|
# We allow debuggers to disagree on exactly how the file
|
|
# will look like since a file corresponds to a scope.
|
|
# The convert_to_markdown() function will check and forbid
|
|
# per-paramater disagreements.
|
|
debugger_to_paramlist: Dict[str, list[ExtractedParam]] = {}
|
|
for debugger, data in extracted:
|
|
if scope in data:
|
|
debugger_to_paramlist[debugger] = data[scope]
|
|
|
|
filename = os.path.join(BASE_PATH, f"{scope}.md")
|
|
result[filename] = convert_to_markdown(scope, debugger_to_paramlist)
|
|
|
|
return result
|
|
|
|
|
|
def read_extracted() -> list[Tuple[str, Dict[str, list[ExtractedParam]]]]:
|
|
"""
|
|
Read json files from disk.
|
|
|
|
Returns:
|
|
A list of tuples of the form: (debugger name, scope-mapped
|
|
extracted parameters for that debugger).
|
|
"""
|
|
result: list[Tuple[str, Dict[str, ExtractedParam]]] = []
|
|
|
|
for debugger in ALL_DEBUGGERS:
|
|
filepath = extracted_filename(debugger)
|
|
print(f"Consuming {filepath}..")
|
|
|
|
with open(filepath, "r") as file:
|
|
raw_data = json.loads(file.read())
|
|
|
|
# Convert the dict objs to ExtractedParams
|
|
data: Dict[str, ExtractedParam] = {}
|
|
for scope, param_list in raw_data.items():
|
|
data[scope] = [ExtractedParam(**param_dict) for param_dict in param_list]
|
|
|
|
result.append((debugger, data))
|
|
|
|
# We consumed the temporary file, we can delete it now.
|
|
os.remove(filepath)
|
|
|
|
return result
|
|
|
|
|
|
# NOTE: the docs/configuration/index.md file is
|
|
# not autogenerated.
|
|
INDEX_PATH = os.path.join(BASE_PATH, "index.md")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) > 1:
|
|
print("This script doesn't accept any arguments.")
|
|
print("See top of the file for usage.")
|
|
sys.exit(1)
|
|
|
|
just_verify = False
|
|
if os.getenv("PWNDBG_DOCGEN_VERIFY"):
|
|
just_verify = True
|
|
|
|
print("\n==== Parameter Documentation ====")
|
|
|
|
extracted = read_extracted()
|
|
markdowned = convert_all_to_markdown(extracted)
|
|
|
|
if just_verify:
|
|
print("Checking if all files are in place..")
|
|
missing, extra = verify_existence(list(markdowned.keys()) + [INDEX_PATH], BASE_PATH)
|
|
if missing or extra:
|
|
print("To add mising files please run ./scripts/generate-docs.sh.")
|
|
print("To remove extra files please remove them manually.")
|
|
sys.exit(2)
|
|
print("Every file is where it should be!")
|
|
|
|
print("Verifying contents...")
|
|
err = verify_files_simple(markdowned, skip=[INDEX_PATH])
|
|
if err:
|
|
print("VERIFICATION FAILED. The files differ from what would be auto-generated.")
|
|
print("Error:", err)
|
|
print("Please run ./scripts/generate-docs.sh from project root and commit the changes.")
|
|
sys.exit(3)
|
|
|
|
print("Verification successful!")
|
|
else:
|
|
print("Updating files...")
|
|
update_files_simple(markdowned)
|
|
print("Update successful.")
|
|
|
|
missing, extra = verify_existence(list(markdowned.keys()) + [INDEX_PATH], BASE_PATH)
|
|
if len(missing) == 1 and missing[0] == INDEX_PATH:
|
|
print(
|
|
f"The index ({INDEX_PATH}) is missing. That is a hand-written file, please write it."
|
|
)
|
|
sys.exit(4)
|
|
|
|
assert (
|
|
not missing
|
|
and "Some files (and not the index) are missing, which should be impossible."
|
|
)
|
|
|
|
if extra:
|
|
sys.exit(5)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|