aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/llvm-original-di-preservation.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/utils/llvm-original-di-preservation.py')
-rwxr-xr-xllvm/utils/llvm-original-di-preservation.py154
1 files changed, 121 insertions, 33 deletions
diff --git a/llvm/utils/llvm-original-di-preservation.py b/llvm/utils/llvm-original-di-preservation.py
index 03793b1..b5ccd7a 100755
--- a/llvm/utils/llvm-original-di-preservation.py
+++ b/llvm/utils/llvm-original-di-preservation.py
@@ -11,7 +11,6 @@ from json import loads
from collections import defaultdict
from collections import OrderedDict
-
class DILocBug:
def __init__(self, origin, action, bb_name, fn_name, instr):
self.origin = origin
@@ -20,18 +19,35 @@ class DILocBug:
self.fn_name = fn_name
self.instr = instr
- def __str__(self):
+ def key(self):
return self.action + self.bb_name + self.fn_name + self.instr
+ def to_dict(self):
+ result = {
+ "instr": self.instr,
+ "fn_name": self.fn_name,
+ "bb_name": self.bb_name,
+ "action": self.action,
+ }
+ if self.origin:
+ result["origin"] = self.origin
+ return result
+
class DISPBug:
def __init__(self, action, fn_name):
self.action = action
self.fn_name = fn_name
- def __str__(self):
+ def key(self):
return self.action + self.fn_name
+ def to_dict(self):
+ return {
+ "fn_name": self.fn_name,
+ "action": self.action,
+ }
+
class DIVarBug:
def __init__(self, action, name, fn_name):
@@ -39,9 +55,41 @@ class DIVarBug:
self.name = name
self.fn_name = fn_name
- def __str__(self):
+ def key(self):
return self.action + self.name + self.fn_name
+ def to_dict(self):
+ return {
+ "fn_name": self.fn_name,
+ "name": self.name,
+ "action": self.action,
+ }
+
+
+def print_bugs_yaml(name, bugs_dict, indent=2):
+ def get_bug_line(indent_level: int, text: str, margin_mark: bool = False):
+ if margin_mark:
+ return "- ".rjust(indent_level * indent) + text
+ return " " * indent * indent_level + text
+
+ print(f"{name}:")
+ for bugs_file, bugs_pass_dict in sorted(iter(bugs_dict.items())):
+ print(get_bug_line(1, f"{bugs_file}:"))
+ for bugs_pass, bugs_list in sorted(iter(bugs_pass_dict.items())):
+ print(get_bug_line(2, f"{bugs_pass}:"))
+ for bug in bugs_list:
+ bug_dict = bug.to_dict()
+ first_line = True
+ # First item needs a '-' in the margin.
+ for key, val in sorted(iter(bug_dict.items())):
+ if "\n" in val:
+ # Output block text for any multiline string.
+ print(get_bug_line(3, f"{key}: |", first_line))
+ for line in val.splitlines():
+ print(get_bug_line(4, line))
+ else:
+ print(get_bug_line(3, f"{key}: {val}", first_line))
+ first_line = False
# Report the bugs in form of html.
def generate_html_report(
@@ -430,9 +478,16 @@ def get_json_chunk(file, start, size):
# Parse the program arguments.
def parse_program_args(parser):
parser.add_argument("file_name", type=str, help="json file to process")
- parser.add_argument("html_file", type=str, help="html file to output data")
- parser.add_argument(
- "-compress", action="store_true", help="create reduced html report"
+ parser.add_argument("--reduce", action="store_true", help="create reduced report")
+
+ report_type_group = parser.add_mutually_exclusive_group(required=True)
+ report_type_group.add_argument(
+ "--report-html-file", type=str, help="output HTML file for the generated report"
+ )
+ report_type_group.add_argument(
+ "--acceptance-test",
+ action="store_true",
+ help="if set, produce terminal-friendly output and return 0 iff the input file is empty or does not exist",
)
return parser.parse_args()
@@ -442,10 +497,22 @@ def Main():
parser = argparse.ArgumentParser()
opts = parse_program_args(parser)
- if not opts.html_file.endswith(".html"):
+ if opts.report_html_file is not None and not opts.report_html_file.endswith(
+ ".html"
+ ):
print("error: The output file must be '.html'.")
sys.exit(1)
+ if opts.acceptance_test:
+ if os.path.isdir(opts.file_name):
+ print(f"error: Directory passed as input file: '{opts.file_name}'")
+ sys.exit(1)
+ if not os.path.exists(opts.file_name):
+ # We treat an empty input file as a success, as debugify will generate an output file iff any errors are
+ # found, meaning we expect 0 errors to mean that the expected file does not exist.
+ print(f"No errors detected for: {opts.file_name}")
+ sys.exit(0)
+
# Use the defaultdict in order to make multidim dicts.
di_location_bugs = defaultdict(lambda: defaultdict(list))
di_subprogram_bugs = defaultdict(lambda: defaultdict(list))
@@ -489,9 +556,9 @@ def Main():
skipped_lines += 1
continue
- di_loc_bugs = di_location_bugs[bugs_file][bugs_pass]
- di_sp_bugs = di_subprogram_bugs[bugs_file][bugs_pass]
- di_var_bugs = di_variable_bugs[bugs_file][bugs_pass]
+ di_loc_bugs = di_location_bugs.get("bugs_file", {}).get("bugs_pass", [])
+ di_sp_bugs = di_subprogram_bugs.get("bugs_file", {}).get("bugs_pass", [])
+ di_var_bugs = di_variable_bugs.get("bugs_file", {}).get("bugs_pass", [])
# Omit duplicated bugs.
di_loc_set = set()
@@ -515,9 +582,9 @@ def Main():
skipped_bugs += 1
continue
di_loc_bug = DILocBug(origin, action, bb_name, fn_name, instr)
- if not str(di_loc_bug) in di_loc_set:
- di_loc_set.add(str(di_loc_bug))
- if opts.compress:
+ if not di_loc_bug.key() in di_loc_set:
+ di_loc_set.add(di_loc_bug.key())
+ if opts.reduce:
pass_instr = bugs_pass + instr
if not pass_instr in di_loc_pass_instr_set:
di_loc_pass_instr_set.add(pass_instr)
@@ -538,9 +605,9 @@ def Main():
skipped_bugs += 1
continue
di_sp_bug = DISPBug(action, name)
- if not str(di_sp_bug) in di_sp_set:
- di_sp_set.add(str(di_sp_bug))
- if opts.compress:
+ if not di_sp_bug.key() in di_sp_set:
+ di_sp_set.add(di_sp_bug.key())
+ if opts.reduce:
pass_fn = bugs_pass + name
if not pass_fn in di_sp_pass_fn_set:
di_sp_pass_fn_set.add(pass_fn)
@@ -562,9 +629,9 @@ def Main():
skipped_bugs += 1
continue
di_var_bug = DIVarBug(action, name, fn_name)
- if not str(di_var_bug) in di_var_set:
- di_var_set.add(str(di_var_bug))
- if opts.compress:
+ if not di_var_bug.key() in di_var_set:
+ di_var_set.add(di_var_bug.key())
+ if opts.reduce:
pass_var = bugs_pass + name
if not pass_var in di_var_pass_var_set:
di_var_pass_var_set.add(pass_var)
@@ -582,19 +649,40 @@ def Main():
skipped_bugs += 1
continue
- di_location_bugs[bugs_file][bugs_pass] = di_loc_bugs
- di_subprogram_bugs[bugs_file][bugs_pass] = di_sp_bugs
- di_variable_bugs[bugs_file][bugs_pass] = di_var_bugs
-
- generate_html_report(
- di_location_bugs,
- di_subprogram_bugs,
- di_variable_bugs,
- di_location_bugs_summary,
- di_sp_bugs_summary,
- di_var_bugs_summary,
- opts.html_file,
- )
+ if di_loc_bugs:
+ di_location_bugs[bugs_file][bugs_pass] = di_loc_bugs
+ if di_sp_bugs:
+ di_subprogram_bugs[bugs_file][bugs_pass] = di_sp_bugs
+ if di_var_bugs:
+ di_variable_bugs[bugs_file][bugs_pass] = di_var_bugs
+
+ if opts.report_html_file is not None:
+ generate_html_report(
+ di_location_bugs,
+ di_subprogram_bugs,
+ di_variable_bugs,
+ di_location_bugs_summary,
+ di_sp_bugs_summary,
+ di_var_bugs_summary,
+ opts.report_html_file,
+ )
+ else:
+ # Pretty(ish) print the detected bugs, but check if any exist first so that we don't print an empty dict.
+ if di_location_bugs:
+ print_bugs_yaml("DILocation Bugs", di_location_bugs)
+ if di_subprogram_bugs:
+ print_bugs_yaml("DISubprogram Bugs", di_subprogram_bugs)
+ if di_variable_bugs:
+ print_bugs_yaml("DIVariable Bugs", di_variable_bugs)
+
+ if opts.acceptance_test:
+ if any((di_location_bugs, di_subprogram_bugs, di_variable_bugs)):
+ # Add a newline gap after printing at least one error.
+ print()
+ print(f"Errors detected for: {opts.file_name}")
+ sys.exit(1)
+ else:
+ print(f"No errors detected for: {opts.file_name}")
if skipped_lines > 0:
print("Skipped lines: " + str(skipped_lines))