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
49            expectation_val = compare_frame.read_var("expectation")
50            check_literal = expectation_val.string(encoding="utf-8")
51            if "PrettyPrintToRegex" in compare_frame.name():
52                test_fails = not re.match(check_literal, value)
53            else:
54                test_fails = value != check_literal
55
56            if test_fails:
57                global test_failures
58                print("FAIL: " + test_loc.symtab.filename +
59                      ":" + str(test_loc.line))
60                print("GDB printed:")
61                print("   " + repr(value))
62                print("Value should match:")
63                print("   " + repr(check_literal))
64                test_failures += 1
65            else:
66                print("PASS: " + test_loc.symtab.filename +
67                      ":" + str(test_loc.line))
68
69        except RuntimeError as e:
70            # At this point, lots of different things could be wrong, so don't try to
71            # recover or figure it out. Don't exit either, because then it's
72            # impossible debug the framework itself.
73            print("FAIL: Something is wrong in the test framework.")
74            print(str(e))
75            test_failures += 1
76
77    def _get_value_string(self, compare_frame, testcase_frame):
78        compare_frame.select()
79        if "ComparePrettyPrint" in compare_frame.name():
80            s = gdb.execute("p value", to_string=True)
81        else:
82            value_str = str(compare_frame.read_var("value"))
83            clean_expression_str = value_str.strip("'\"")
84            testcase_frame.select()
85            s = gdb.execute("p " + clean_expression_str, to_string=True)
86        if sys.version_info.major == 2:
87            return s.decode("utf-8")
88        return s
89
90
91def exit_handler(event=None):
92    global test_failures
93    if test_failures:
94        print("FAILED %d cases" % test_failures)
95    exit(test_failures)
96
97
98# Start code executed at load time
99
100# Disable terminal paging
101gdb.execute("set height 0")
102gdb.execute("set python print-stack full")
103test_failures = 0
104CheckResult()
105test_bp = gdb.Breakpoint("StopForDebugger")
106test_bp.enabled = True
107test_bp.silent = True
108test_bp.commands = "print_and_compare\ncontinue"
109# "run" won't return if the program exits; ensure the script regains control.
110gdb.events.exited.connect(exit_handler)
111gdb.execute("run")
112# If the program didn't exit, something went wrong, but we don't
113# know what. Fail on exit.
114test_failures += 1
115exit_handler(None)
116