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# Sometimes the inital run command can fail to trace the process. 24# (e.g. you don't have ptrace permissions) 25# In these cases gdb still sends us an exited event so we cannot 26# see what "run" printed to check for a warning message, since 27# we get taken to our exit handler before we can look. 28# Instead check that at least one test has been run by the time 29# we exit. 30has_run_tests = False 31 32 33class CheckResult(gdb.Command): 34 def __init__(self): 35 super(CheckResult, self).__init__("print_and_compare", gdb.COMMAND_DATA) 36 37 def invoke(self, arg, from_tty): 38 global has_run_tests 39 40 try: 41 has_run_tests = True 42 43 # Stack frame is: 44 # 0. StopForDebugger 45 # 1. ComparePrettyPrintToChars or ComparePrettyPrintToRegex 46 # 2. TestCase 47 compare_frame = gdb.newest_frame().older() 48 testcase_frame = compare_frame.older() 49 test_loc = testcase_frame.find_sal() 50 # Use interactive commands in the correct context to get the pretty 51 # printed version 52 53 value_str = self._get_value_string(compare_frame, testcase_frame) 54 55 # Ignore the convenience variable name and newline 56 value = value_str[value_str.find("= ") + 2 : -1] 57 gdb.newest_frame().select() 58 expectation_val = compare_frame.read_var("expectation") 59 check_literal = expectation_val.string(encoding="utf-8") 60 if "PrettyPrintToRegex" in compare_frame.name(): 61 test_fails = not re.search(check_literal, value) 62 else: 63 test_fails = value != check_literal 64 65 if test_fails: 66 global test_failures 67 print("FAIL: " + test_loc.symtab.filename + ":" + str(test_loc.line)) 68 print("GDB printed:") 69 print(" " + repr(value)) 70 print("Value should match:") 71 print(" " + repr(check_literal)) 72 test_failures += 1 73 else: 74 print("PASS: " + test_loc.symtab.filename + ":" + str(test_loc.line)) 75 76 except RuntimeError as e: 77 # At this point, lots of different things could be wrong, so don't try to 78 # recover or figure it out. Don't exit either, because then it's 79 # impossible to debug the framework itself. 80 print("FAIL: Something is wrong in the test framework.") 81 print(str(e)) 82 test_failures += 1 83 84 def _get_value_string(self, compare_frame, testcase_frame): 85 compare_frame.select() 86 if "ComparePrettyPrint" in compare_frame.name(): 87 s = gdb.execute("p value", to_string=True) 88 else: 89 value_str = str(compare_frame.read_var("value")) 90 clean_expression_str = value_str.strip("'\"") 91 testcase_frame.select() 92 s = gdb.execute("p " + clean_expression_str, to_string=True) 93 if sys.version_info.major == 2: 94 return s.decode("utf-8") 95 return s 96 97 98def exit_handler(event=None): 99 global test_failures 100 global has_run_tests 101 102 if not has_run_tests: 103 print("FAILED test program did not run correctly, check gdb warnings") 104 test_failures = -1 105 elif test_failures: 106 print("FAILED %d cases" % test_failures) 107 exit(test_failures) 108 109 110# Start code executed at load time 111 112# Disable terminal paging 113gdb.execute("set height 0") 114gdb.execute("set python print-stack full") 115test_failures = 0 116CheckResult() 117test_bp = gdb.Breakpoint("StopForDebugger") 118test_bp.enabled = True 119test_bp.silent = True 120test_bp.commands = "print_and_compare\ncontinue" 121# "run" won't return if the program exits; ensure the script regains control. 122gdb.events.exited.connect(exit_handler) 123gdb.execute("run") 124# If the program didn't exit, something went wrong, but we don't 125# know what. Fail on exit. 126test_failures += 1 127exit_handler(None) 128