xref: /llvm-project/lldb/packages/Python/lldbsuite/test/lldbinline.py (revision 968bb72f5869fe524c64ab520f66b05a57c3f561)
1from __future__ import print_function
2from __future__ import absolute_import
3
4# System modules
5import filecmp
6import os
7import sys
8
9# Third-party modules
10
11# LLDB modules
12import lldb
13from .lldbtest import *
14from . import configuration
15from . import lldbutil
16from .decorators import *
17
18def source_type(filename):
19    _, extension = os.path.splitext(filename)
20    return {
21        '.c' : 'C_SOURCES',
22        '.cpp' : 'CXX_SOURCES',
23        '.cxx' : 'CXX_SOURCES',
24        '.cc' : 'CXX_SOURCES',
25        '.m' : 'OBJC_SOURCES',
26        '.mm' : 'OBJCXX_SOURCES'
27    }.get(extension, None)
28
29
30class CommandParser:
31    def __init__(self):
32        self.breakpoints = []
33
34    def parse_one_command(self, line):
35        parts = line.split('//%')
36
37        command = None
38        new_breakpoint = True
39
40        if len(parts) == 2:
41            command = parts[1].strip()  # take off whitespace
42            new_breakpoint = parts[0].strip() != ""
43
44        return (command, new_breakpoint)
45
46    def parse_source_files(self, source_files):
47        for source_file in source_files:
48            file_handle = open(source_file)
49            lines = file_handle.readlines()
50            line_number = 0
51            current_breakpoint = None # non-NULL means we're looking through whitespace to find additional commands
52            for line in lines:
53                line_number = line_number + 1 # 1-based, so we do this first
54                (command, new_breakpoint) = self.parse_one_command(line)
55
56                if new_breakpoint:
57                    current_breakpoint = None
58
59                if command != None:
60                    if current_breakpoint == None:
61                        current_breakpoint = {}
62                        current_breakpoint['file_name'] = source_file
63                        current_breakpoint['line_number'] = line_number
64                        current_breakpoint['command'] = command
65                        self.breakpoints.append(current_breakpoint)
66                    else:
67                        current_breakpoint['command'] = current_breakpoint['command'] + "\n" + command
68
69    def set_breakpoints(self, target):
70        for breakpoint in self.breakpoints:
71            breakpoint['breakpoint'] = target.BreakpointCreateByLocation(breakpoint['file_name'], breakpoint['line_number'])
72
73    def handle_breakpoint(self, test, breakpoint_id):
74        for breakpoint in self.breakpoints:
75            if breakpoint['breakpoint'].GetID() == breakpoint_id:
76                test.execute_user_command(breakpoint['command'])
77                return
78
79class InlineTest(TestBase):
80    # Internal implementation
81
82    def getRerunArgs(self):
83        # The -N option says to NOT run a if it matches the option argument, so
84        # if we are using dSYM we say to NOT run dwarf (-N dwarf) and vice versa.
85        if self.using_dsym is None:
86            # The test was skipped altogether.
87            return ""
88        elif self.using_dsym:
89            return "-N dwarf %s" % (self.mydir)
90        else:
91            return "-N dsym %s" % (self.mydir)
92
93    def BuildMakefile(self):
94        categories = {}
95
96        for f in os.listdir(os.getcwd()):
97            t = source_type(f)
98            if t:
99                if t in list(categories.keys()):
100                    categories[t].append(f)
101                else:
102                    categories[t] = [f]
103
104        makefile = open("Makefile.tmp", 'w+')
105
106        level = os.sep.join([".."] * len(self.mydir.split(os.sep))) + os.sep + "make"
107
108        makefile.write("LEVEL = " + level + "\n")
109
110        for t in list(categories.keys()):
111            line = t + " := " + " ".join(categories[t])
112            makefile.write(line + "\n")
113
114        if ('OBJCXX_SOURCES' in list(categories.keys())) or ('OBJC_SOURCES' in list(categories.keys())):
115            makefile.write("LDFLAGS = $(CFLAGS) -lobjc -framework Foundation\n")
116
117        if ('CXX_SOURCES' in list(categories.keys())):
118            makefile.write("CXXFLAGS += -std=c++11\n")
119
120        # clang-3.5+ outputs FullDebugInfo by default for Darwin/FreeBSD
121        # targets.  Other targets do not, which causes this test to fail.
122        # This flag enables FullDebugInfo for all targets.
123
124        makefile.write("ifneq (,$(findstring clang,$(CC)))\n")
125        makefile.write("    CFLAGS_EXTRAS += -fno-limit-debug-info\n")
126        makefile.write("endif\n\n")
127
128        makefile.write("include $(LEVEL)/Makefile.rules\n\n")
129
130        makefile.write("\ncleanup:\n\trm -f Makefile *.d\n\n")
131        makefile.flush()
132        makefile.close()
133
134        if os.path.exists("Makefile"):
135            if not filecmp.cmp("Makefile", "Makefile.tmp"):
136                sys.exit("Existing Makefile doesn't match generated Makefile!")
137
138        os.rename("Makefile.tmp", "Makefile")
139
140    @skipUnlessDarwin
141    def __test_with_dsym(self):
142        self.using_dsym = True
143        self.BuildMakefile()
144        self.buildDsym()
145        self.do_test()
146
147    def __test_with_dwarf(self):
148        self.using_dsym = False
149        self.BuildMakefile()
150        self.buildDwarf()
151        self.do_test()
152
153    def __test_with_dwo(self):
154        self.using_dsym = False
155        self.BuildMakefile()
156        self.buildDwo()
157        self.do_test()
158
159    def __test_with_gmodules(self):
160        self.using_dsym = False
161        self.BuildMakefile()
162        self.buildGModules()
163        self.do_test()
164
165    def execute_user_command(self, __command):
166        exec(__command, globals(), locals())
167
168    def do_test(self):
169        exe_name = "a.out"
170        exe = os.path.join(os.getcwd(), exe_name)
171        source_files = [ f for f in os.listdir(os.getcwd()) if source_type(f) ]
172        target = self.dbg.CreateTarget(exe)
173
174        parser = CommandParser()
175        parser.parse_source_files(source_files)
176        parser.set_breakpoints(target)
177
178        process = target.LaunchSimple(None, None, os.getcwd())
179
180        while lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint):
181            thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
182            breakpoint_id = thread.GetStopReasonDataAtIndex (0)
183            parser.handle_breakpoint(self, breakpoint_id)
184            process.Continue()
185
186
187    # Utilities for testcases
188
189    def check_expression (self, expression, expected_result, use_summary = True):
190        value = self.frame().EvaluateExpression (expression)
191        self.assertTrue(value.IsValid(), expression+"returned a valid value")
192        if self.TraceOn():
193            print(value.GetSummary())
194            print(value.GetValue())
195        if use_summary:
196            answer = value.GetSummary()
197        else:
198            answer = value.GetValue()
199        report_str = "%s expected: %s got: %s"%(expression, expected_result, answer)
200        self.assertTrue(answer == expected_result, report_str)
201
202def ApplyDecoratorsToFunction(func, decorators):
203    tmp = func
204    if type(decorators) == list:
205        for decorator in decorators:
206            tmp = decorator(tmp)
207    elif hasattr(decorators, '__call__'):
208        tmp = decorators(tmp)
209    return tmp
210
211
212def MakeInlineTest(__file, __globals, decorators=None):
213    # Adjust the filename if it ends in .pyc.  We want filenames to
214    # reflect the source python file, not the compiled variant.
215    if __file is not None and __file.endswith(".pyc"):
216        # Strip the trailing "c"
217        __file = __file[0:-1]
218
219    # Derive the test name from the current file name
220    file_basename = os.path.basename(__file)
221    InlineTest.mydir = TestBase.compute_mydir(__file)
222
223    test_name, _ = os.path.splitext(file_basename)
224    # Build the test case
225    test = type(test_name, (InlineTest,), {'using_dsym': None})
226    test.name = test_name
227
228    target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
229    if test_categories.is_supported_on_platform("dsym", target_platform, configuration.compilers):
230        test.test_with_dsym = ApplyDecoratorsToFunction(test._InlineTest__test_with_dsym, decorators)
231    if test_categories.is_supported_on_platform("dwarf", target_platform, configuration.compilers):
232        test.test_with_dwarf = ApplyDecoratorsToFunction(test._InlineTest__test_with_dwarf, decorators)
233    if test_categories.is_supported_on_platform("dwo", target_platform, configuration.compilers):
234        test.test_with_dwo = ApplyDecoratorsToFunction(test._InlineTest__test_with_dwo, decorators)
235    if test_categories.is_supported_on_platform("gmodules", target_platform, configuration.compilers):
236        test.test_with_gmodules = ApplyDecoratorsToFunction(test._InlineTest__test_with_gmodules, decorators)
237
238    # Add the test case to the globals, and hide InlineTest
239    __globals.update({test_name : test})
240
241    # Keep track of the original test filename so we report it
242    # correctly in test results.
243    test.test_filename = __file
244    return test
245
246