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 41 expectation_val = compare_frame.read_var("expectation") 42 check_literal = expectation_val.string(encoding="utf-8") 43 44 # Heuristic to determine if libc++ itself has debug 45 # info. If it doesn't, then anything normally homed there 46 # won't be found, and the printer will error. We don't 47 # want to fail the test in this case--the printer itself 48 # is probably fine, or at least we can't tell. 49 if check_literal.startswith("std::shared_ptr"): 50 shared_ptr = compare_frame.read_var("value") 51 if not "__shared_owners_" in shared_ptr.type.fields(): 52 print("IGNORED (no debug info in libc++): " + 53 test_loc.symtab.filename + ":" + 54 str(test_loc.line)) 55 return 56 57 # Use interactive commands in the correct context to get the pretty 58 # printed version 59 60 value_str = self._get_value_string(compare_frame, testcase_frame) 61 62 # Ignore the convenience variable name and newline 63 value = value_str[value_str.find("= ") + 2:-1] 64 gdb.newest_frame().select() 65 expectation_val = compare_frame.read_var("expectation") 66 check_literal = expectation_val.string(encoding="utf-8") 67 if "PrettyPrintToRegex" in compare_frame.name(): 68 test_fails = not re.search(check_literal, value) 69 else: 70 test_fails = value != check_literal 71 72 if test_fails: 73 global test_failures 74 print("FAIL: " + test_loc.symtab.filename + 75 ":" + str(test_loc.line)) 76 print("GDB printed:") 77 print(" " + repr(value)) 78 print("Value should match:") 79 print(" " + repr(check_literal)) 80 test_failures += 1 81 else: 82 print("PASS: " + test_loc.symtab.filename + 83 ":" + str(test_loc.line)) 84 85 except RuntimeError as e: 86 # At this point, lots of different things could be wrong, so don't try to 87 # recover or figure it out. Don't exit either, because then it's 88 # impossible debug the framework itself. 89 print("FAIL: Something is wrong in the test framework.") 90 print(str(e)) 91 test_failures += 1 92 93 def _get_value_string(self, compare_frame, testcase_frame): 94 compare_frame.select() 95 if "ComparePrettyPrint" in compare_frame.name(): 96 s = gdb.execute("p value", to_string=True) 97 else: 98 value_str = str(compare_frame.read_var("value")) 99 clean_expression_str = value_str.strip("'\"") 100 testcase_frame.select() 101 s = gdb.execute("p " + clean_expression_str, to_string=True) 102 if sys.version_info.major == 2: 103 return s.decode("utf-8") 104 return s 105 106 107def exit_handler(event=None): 108 global test_failures 109 if test_failures: 110 print("FAILED %d cases" % test_failures) 111 exit(test_failures) 112 113 114# Start code executed at load time 115 116# Disable terminal paging 117gdb.execute("set height 0") 118gdb.execute("set python print-stack full") 119test_failures = 0 120CheckResult() 121test_bp = gdb.Breakpoint("StopForDebugger") 122test_bp.enabled = True 123test_bp.silent = True 124test_bp.commands = "print_and_compare\ncontinue" 125# "run" won't return if the program exits; ensure the script regains control. 126gdb.events.exited.connect(exit_handler) 127gdb.execute("run") 128# If the program didn't exit, something went wrong, but we don't 129# know what. Fail on exit. 130test_failures += 1 131exit_handler(None) 132