1#===----------------------------------------------------------------------===## 2# 3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4# See https://llvm.org/LICENSE.txt for license information. 5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6# 7#===----------------------------------------------------------------------===## 8"""Commands used to automate testing gdb pretty printers. 9 10This script is part of a larger framework to test gdb pretty printers. It 11runs the program, detects test cases, checks them, and prints results. 12 13See gdb_pretty_printer_test.sh.cpp on how to write a test case. 14 15""" 16 17from __future__ import print_function 18import re 19import gdb 20import sys 21 22test_failures = 0 23 24 25class CheckResult(gdb.Command): 26 27 def __init__(self): 28 super(CheckResult, self).__init__( 29 "print_and_compare", gdb.COMMAND_DATA) 30 31 def invoke(self, arg, from_tty): 32 try: 33 # Stack frame is: 34 # 0. StopForDebugger 35 # 1. ComparePrettyPrintToChars or ComparePrettyPrintToRegex 36 # 2. TestCase 37 compare_frame = gdb.newest_frame().older() 38 testcase_frame = compare_frame.older() 39 test_loc = testcase_frame.find_sal() 40 # Use interactive commands in the correct context to get the pretty 41 # printed version 42 43 value_str = self._get_value_string(compare_frame, testcase_frame) 44 45 # Ignore the convenience variable name and newline 46 value = value_str[value_str.find("= ") + 2:-1] 47 gdb.newest_frame().select() 48 expectation_val = compare_frame.read_var("expectation") 49 check_literal = expectation_val.string(encoding="utf-8") 50 if "PrettyPrintToRegex" in compare_frame.name(): 51 test_fails = not re.search(check_literal, value) 52 else: 53 test_fails = value != check_literal 54 55 if test_fails: 56 global test_failures 57 print("FAIL: " + test_loc.symtab.filename + 58 ":" + str(test_loc.line)) 59 print("GDB printed:") 60 print(" " + repr(value)) 61 print("Value should match:") 62 print(" " + repr(check_literal)) 63 test_failures += 1 64 else: 65 print("PASS: " + test_loc.symtab.filename + 66 ":" + str(test_loc.line)) 67 68 except RuntimeError as e: 69 # At this point, lots of different things could be wrong, so don't try to 70 # recover or figure it out. Don't exit either, because then it's 71 # impossible debug the framework itself. 72 print("FAIL: Something is wrong in the test framework.") 73 print(str(e)) 74 test_failures += 1 75 76 def _get_value_string(self, compare_frame, testcase_frame): 77 compare_frame.select() 78 if "ComparePrettyPrint" in compare_frame.name(): 79 s = gdb.execute("p value", to_string=True) 80 else: 81 value_str = str(compare_frame.read_var("value")) 82 clean_expression_str = value_str.strip("'\"") 83 testcase_frame.select() 84 s = gdb.execute("p " + clean_expression_str, to_string=True) 85 if sys.version_info.major == 2: 86 return s.decode("utf-8") 87 return s 88 89 90def exit_handler(event=None): 91 global test_failures 92 if test_failures: 93 print("FAILED %d cases" % test_failures) 94 exit(test_failures) 95 96 97# Start code executed at load time 98 99# Disable terminal paging 100gdb.execute("set height 0") 101gdb.execute("set python print-stack full") 102test_failures = 0 103CheckResult() 104test_bp = gdb.Breakpoint("StopForDebugger") 105test_bp.enabled = True 106test_bp.silent = True 107test_bp.commands = "print_and_compare\ncontinue" 108# "run" won't return if the program exits; ensure the script regains control. 109gdb.events.exited.connect(exit_handler) 110gdb.execute("run") 111# If the program didn't exit, something went wrong, but we don't 112# know what. Fail on exit. 113test_failures += 1 114exit_handler(None) 115