xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/libcxx/gdb/gdb_pretty_printer_test.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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