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