Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lldb / examples / python / types.py
blobe5b4766e8c0d8952c32e9d98b121abd279c0fa66
1 #!/usr/bin/env python
3 # ----------------------------------------------------------------------
4 # Be sure to add the python path that points to the LLDB shared library.
6 # # To use this in the embedded python interpreter using "lldb" just
7 # import it with the full path using the "command script import"
8 # command
9 # (lldb) command script import /path/to/cmdtemplate.py
10 # ----------------------------------------------------------------------
12 import platform
13 import os
14 import re
15 import signal
16 import sys
17 import subprocess
19 try:
20 # Just try for LLDB in case PYTHONPATH is already correctly setup
21 import lldb
22 except ImportError:
23 lldb_python_dirs = list()
24 # lldb is not in the PYTHONPATH, try some defaults for the current platform
25 platform_system = platform.system()
26 if platform_system == "Darwin":
27 # On Darwin, try the currently selected Xcode directory
28 xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)
29 if xcode_dir:
30 lldb_python_dirs.append(
31 os.path.realpath(
32 xcode_dir + "/../SharedFrameworks/LLDB.framework/Resources/Python"
35 lldb_python_dirs.append(
36 xcode_dir + "/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
38 lldb_python_dirs.append(
39 "/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
41 success = False
42 for lldb_python_dir in lldb_python_dirs:
43 if os.path.exists(lldb_python_dir):
44 if not (sys.path.__contains__(lldb_python_dir)):
45 sys.path.append(lldb_python_dir)
46 try:
47 import lldb
48 except ImportError:
49 pass
50 else:
51 print('imported lldb from: "%s"' % (lldb_python_dir))
52 success = True
53 break
54 if not success:
55 print(
56 "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
58 sys.exit(1)
60 import optparse
61 import shlex
62 import time
65 def regex_option_callback(option, opt_str, value, parser):
66 if opt_str == "--std":
67 value = "^std::"
68 regex = re.compile(value)
69 parser.values.skip_type_regexes.append(regex)
72 def create_types_options(for_lldb_command):
73 if for_lldb_command:
74 usage = "usage: %prog [options]"
75 description = """This command will help check for padding in between
76 base classes and members in structs and classes. It will summarize the types
77 and how much padding was found. If no types are specified with the --types TYPENAME
78 option, all structure and class types will be verified. If no modules are
79 specified with the --module option, only the target's main executable will be
80 searched.
81 """
82 else:
83 usage = "usage: %prog [options] EXEPATH [EXEPATH ...]"
84 description = """This command will help check for padding in between
85 base classes and members in structures and classes. It will summarize the types
86 and how much padding was found. One or more paths to executable files must be
87 specified and targets will be created with these modules. If no types are
88 specified with the --types TYPENAME option, all structure and class types will
89 be verified in all specified modules.
90 """
91 parser = optparse.OptionParser(
92 description=description, prog="framestats", usage=usage
94 if not for_lldb_command:
95 parser.add_option(
96 "-a",
97 "--arch",
98 type="string",
99 dest="arch",
100 help="The architecture to use when creating the debug target.",
101 default=None,
103 parser.add_option(
104 "-p",
105 "--platform",
106 type="string",
107 metavar="platform",
108 dest="platform",
109 help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".',
111 parser.add_option(
112 "-m",
113 "--module",
114 action="append",
115 type="string",
116 metavar="MODULE",
117 dest="modules",
118 help="Specify one or more modules which will be used to verify the types.",
119 default=[],
121 parser.add_option(
122 "-d",
123 "--debug",
124 action="store_true",
125 dest="debug",
126 help="Pause 10 seconds to wait for a debugger to attach.",
127 default=False,
129 parser.add_option(
130 "-t",
131 "--type",
132 action="append",
133 type="string",
134 metavar="TYPENAME",
135 dest="typenames",
136 help="Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.",
137 default=[],
139 parser.add_option(
140 "-v",
141 "--verbose",
142 action="store_true",
143 dest="verbose",
144 help="Enable verbose logging and information.",
145 default=False,
147 parser.add_option(
148 "-s",
149 "--skip-type-regex",
150 action="callback",
151 callback=regex_option_callback,
152 type="string",
153 metavar="REGEX",
154 dest="skip_type_regexes",
155 help="Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.",
156 default=[],
158 parser.add_option(
159 "--std",
160 action="callback",
161 callback=regex_option_callback,
162 metavar="REGEX",
163 dest="skip_type_regexes",
164 help="Don't' recurse into types in the std namespace.",
165 default=[],
167 return parser
170 def verify_type(target, options, type):
171 print(type)
172 typename = type.GetName()
173 # print 'type: %s' % (typename)
174 (end_offset, padding) = verify_type_recursive(target, options, type, None, 0, 0, 0)
175 byte_size = type.GetByteSize()
176 # if end_offset < byte_size:
177 # last_member_padding = byte_size - end_offset
178 # print '%+4u <%u> padding' % (end_offset, last_member_padding)
179 # padding += last_member_padding
180 print("Total byte size: %u" % (byte_size))
181 print("Total pad bytes: %u" % (padding))
182 if padding > 0:
183 print(
184 "Padding percentage: %2.2f %%"
185 % ((float(padding) / float(byte_size)) * 100.0)
187 print()
190 def verify_type_recursive(
191 target, options, type, member_name, depth, base_offset, padding
193 prev_end_offset = base_offset
194 typename = type.GetName()
195 byte_size = type.GetByteSize()
196 if member_name and member_name != typename:
197 print(
198 "%+4u <%3u> %s%s %s;"
199 % (base_offset, byte_size, " " * depth, typename, member_name)
201 else:
202 print("%+4u {%3u} %s%s" % (base_offset, byte_size, " " * depth, typename))
204 for type_regex in options.skip_type_regexes:
205 match = type_regex.match(typename)
206 if match:
207 return (base_offset + byte_size, padding)
209 members = type.members
210 if members:
211 for member_idx, member in enumerate(members):
212 member_type = member.GetType()
213 member_canonical_type = member_type.GetCanonicalType()
214 member_type_class = member_canonical_type.GetTypeClass()
215 member_name = member.GetName()
216 member_offset = member.GetOffsetInBytes()
217 member_total_offset = member_offset + base_offset
218 member_byte_size = member_type.GetByteSize()
219 member_is_class_or_struct = False
220 if (
221 member_type_class == lldb.eTypeClassStruct
222 or member_type_class == lldb.eTypeClassClass
224 member_is_class_or_struct = True
225 if (
226 member_idx == 0
227 and member_offset == target.GetAddressByteSize()
228 and type.IsPolymorphicClass()
230 ptr_size = target.GetAddressByteSize()
231 print(
232 "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"
233 % (prev_end_offset, ptr_size, " " * (depth + 1))
235 prev_end_offset = ptr_size
236 else:
237 if prev_end_offset < member_total_offset:
238 member_padding = member_total_offset - prev_end_offset
239 padding = padding + member_padding
240 print(
241 "%+4u <%3u> %s<PADDING>"
242 % (prev_end_offset, member_padding, " " * (depth + 1))
245 if member_is_class_or_struct:
246 (prev_end_offset, padding) = verify_type_recursive(
247 target,
248 options,
249 member_canonical_type,
250 member_name,
251 depth + 1,
252 member_total_offset,
253 padding,
255 else:
256 prev_end_offset = member_total_offset + member_byte_size
257 member_typename = member_type.GetName()
258 if member.IsBitfield():
259 print(
260 "%+4u <%3u> %s%s:%u %s;"
262 member_total_offset,
263 member_byte_size,
264 " " * (depth + 1),
265 member_typename,
266 member.GetBitfieldSizeInBits(),
267 member_name,
270 else:
271 print(
272 "%+4u <%3u> %s%s %s;"
274 member_total_offset,
275 member_byte_size,
276 " " * (depth + 1),
277 member_typename,
278 member_name,
282 if prev_end_offset < byte_size:
283 last_member_padding = byte_size - prev_end_offset
284 print(
285 "%+4u <%3u> %s<PADDING>"
286 % (prev_end_offset, last_member_padding, " " * (depth + 1))
288 padding += last_member_padding
289 else:
290 if type.IsPolymorphicClass():
291 ptr_size = target.GetAddressByteSize()
292 print(
293 "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"
294 % (prev_end_offset, ptr_size, " " * (depth + 1))
296 prev_end_offset = ptr_size
297 prev_end_offset = base_offset + byte_size
299 return (prev_end_offset, padding)
302 def check_padding_command(debugger, command, result, dict):
303 # Use the Shell Lexer to properly parse up command options just like a
304 # shell would
305 command_args = shlex.split(command)
306 parser = create_types_options(True)
307 try:
308 (options, args) = parser.parse_args(command_args)
309 except:
310 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
311 # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
312 result.SetStatus(lldb.eReturnStatusFailed)
313 # returning a string is the same as returning an error whose
314 # description is the string
315 return "option parsing failed"
316 verify_types(debugger.GetSelectedTarget(), options)
319 @lldb.command("parse_all_struct_class_types")
320 def parse_all_struct_class_types(debugger, command, result, dict):
321 command_args = shlex.split(command)
322 for f in command_args:
323 error = lldb.SBError()
324 target = debugger.CreateTarget(f, None, None, False, error)
325 module = target.GetModuleAtIndex(0)
326 print("Parsing all types in '%s'" % (module))
327 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct)
328 for t in types:
329 print(t)
330 print("")
333 def verify_types(target, options):
334 if not target:
335 print("error: invalid target")
336 return
338 modules = list()
339 if len(options.modules) == 0:
340 # Append just the main executable if nothing was specified
341 module = target.modules[0]
342 if module:
343 modules.append(module)
344 else:
345 for module_name in options.modules:
346 module = lldb.target.module[module_name]
347 if module:
348 modules.append(module)
350 if modules:
351 for module in modules:
352 print("module: %s" % (module.file))
353 if options.typenames:
354 for typename in options.typenames:
355 types = module.FindTypes(typename)
356 if types.GetSize():
357 print(
358 'Found %u types matching "%s" in "%s"'
359 % (len(types), typename, module.file)
361 for type in types:
362 verify_type(target, options, type)
363 else:
364 print(
365 'error: no type matches "%s" in "%s"'
366 % (typename, module.file)
368 else:
369 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct)
370 print('Found %u types in "%s"' % (len(types), module.file))
371 for type in types:
372 verify_type(target, options, type)
373 else:
374 print("error: no modules")
377 if __name__ == "__main__":
378 debugger = lldb.SBDebugger.Create()
379 parser = create_types_options(False)
381 # try:
382 (options, args) = parser.parse_args(sys.argv[1:])
383 # except:
384 # print "error: option parsing failed"
385 # sys.exit(1)
387 if options.debug:
388 print("Waiting for debugger to attach to process %d" % os.getpid())
389 os.kill(os.getpid(), signal.SIGSTOP)
391 for path in args:
392 # in a command - the lldb.* convenience variables are not to be used
393 # and their values (if any) are undefined
394 # this is the best practice to access those objects from within a
395 # command
396 error = lldb.SBError()
397 target = debugger.CreateTarget(
398 path, options.arch, options.platform, True, error
400 if error.Fail():
401 print(error.GetCString())
402 continue
403 verify_types(target, options)
406 def __lldb_init_module(debugger, internal_dict):
407 debugger.HandleCommand(
408 "command script add -o -f types.check_padding_command check_padding"
410 print(
411 '"check_padding" command installed, use the "--help" option for detailed help'