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