""" Support for debug info metadata. """ import functools import llvm.core from llvm import _dwarf #---------------------------------------------------------------------------- # Some types and type checking functions #---------------------------------------------------------------------------- int32_t = llvm.core.Type.int(32) bool_t = llvm.core.Type.int(1) p_int32_t = llvm.core.Type.pointer(int32_t) null = llvm.core.Constant.null(p_int32_t) i32 = functools.partial(llvm.core.Constant.int, int32_t) i1 = functools.partial(llvm.core.Constant.int, bool_t) def is_md(value): return isinstance(value, (llvm.core.MetaData, llvm.core.NamedMetaData, llvm.core.Value)) def is_mdstr(value): return isinstance(value, (llvm.core.MetaDataString, llvm.core.Value)) #---------------------------------------------------------------------------- # Callbacks for debug type descriptors #---------------------------------------------------------------------------- def get_i32(llvm_module, value): return i32(value) def get_i1(llvm_module, value): return i1(value) def get_md(llvm_module, value): if is_md(value): return value return value.get_metadata(llvm_module) def get_mdstr(llvm_module, value): if is_mdstr(value): return value return llvm.core.MetaDataString.get(llvm_module, value) def get_mdlist(llvm_module, value): if isinstance(value, list): value = MDList([value]) return get_md(llvm_module, value) def get_lfunc(llvm_module, lfunc): assert lfunc.module is llvm_module return lfunc #---------------------------------------------------------------------------- # Debug descriptors that generate LLVM Metadata #---------------------------------------------------------------------------- class desc(object): """ Descriptor of a debug field. field_name: name of the field (keyword argument name) build_metadata: metadata callback :: (llvm_module, Python value) -> LLVM Value default: default value for this field """ def __init__(self, field_name, build_metadata, default=None): self.field_name = field_name self.build_metadata = build_metadata self.default = default def build_operand_list(type_descriptors, idx2name, operands, kwargs): """ Build the list of operands from the positional and keyword arguments to a DebugInfoDescriptor. E.g. FileDescriptor("foo.c", "/path/to/file", compile_unit=compile_unit) idx2name: {0 : 'source_filename', 1 : 'source_filedir', 2 : 'compile_unit' } operands: ["foo.c", "/path/to/file"] kwargs: { 'compile_unit' : compile_unit } """ for i in range(len(operands), len(type_descriptors)): name = idx2name[i] if name in kwargs: op = kwargs[name] else: typedesc = type_descriptors[i] assert typedesc.default is not None, ( "No value found for field %s" % typedesc.field_name) op = typedesc.default operands.append(op) return operands class DebugInfoDescriptor(object): """ Base class to describe a debug info descriptor. See http://llvm.org/docs/SourceLevelDebugging.html """ type_descriptors = None # [desc(...)] idx2name = None # { "field_idx" : field_name } name2idx = None # { "field_name" : field_idx } # Whether the debug descriptor is allowed to be incomplete # The policy seems unclear ? accept_optional_data = False def __init__(self, *args, **kwargs): self.class_init() self.metadata_cache = {} operands = list(args) if kwargs or not self.accept_optional_data: operands = build_operand_list(self.type_descriptors, self.idx2name, operands, kwargs) self.operands = operands self.operands_dict = dict( (typedesc.field_name, operand) for typedesc, operand in zip(self.type_descriptors, operands)) @classmethod def class_init(cls): if cls.idx2name is None and cls.type_descriptors is not None: cls.name2idx = {} cls.idx2name = {} for i, typedesc in enumerate(cls.type_descriptors): cls.name2idx[typedesc.field_name] = i cls.idx2name[i] = typedesc.field_name def add_metadata(self, metadata_name, metadata): """ Replace a metadata value in the operand list. """ idx = self.name2idx[metadata_name] self.operands[idx] = metadata #------------------------------------------------------------------------ # Define metadata in a module #------------------------------------------------------------------------ def get_metadata(self, llvm_module): """ Get an existing metadata node for the given LLVM module, or build one from this instance. """ if llvm_module in self.metadata_cache: node = self.metadata_cache[llvm_module] else: node = self.build_metadata(llvm_module) self.metadata_cache[llvm_module] = node return node def build_metadata(self, llvm_module): "Build a metadata node for the given LLVM module" mdops = [] for type_desc, operand in zip(self.type_descriptors, self.operands): try: field_value = type_desc.build_metadata(llvm_module, operand) except Exception, e: if type_desc.build_metadata is get_md: raise raise ValueError("Invalid value for field %r: %s" % ( type_desc.field_name, e)) mdops.append(field_value) return llvm.core.MetaData.get(llvm_module, mdops) def define(self, llvm_module): """ Define this debug descriptor as named debug metadata for the module. """ md = self.get_metadata(llvm_module) llvm.core.MetaData.add_named_operand(llvm_module, "dbg", md) #---------------------------------------------------------------------------- # Convenience descriptors #---------------------------------------------------------------------------- class MDList(DebugInfoDescriptor): """ [MetaData] """ def __init__(self, operands): self.type_descriptors = [desc("op", get_md)] * len(operands) super(MDList, self).__init__(*operands) empty = MDList([]) #---------------------------------------------------------------------------- # Dwarf Debug descriptors #---------------------------------------------------------------------------- class CompileUnitDescriptor(DebugInfoDescriptor): """ !0 = metadata !{ i32, ;; Tag = 17 + LLVMDebugVersion (DW_TAG_compile_unit) i32, ;; Unused field. i32, ;; DWARF language identifier (ex. DW_LANG_C89) metadata, ;; Source file name metadata, ;; Source file directory (includes trailing slash) metadata ;; Producer (ex. "4.0.1 LLVM (LLVM research group)") i1, ;; True if this is a main compile unit. i1, ;; True if this is optimized. metadata, ;; Flags i32 ;; Runtime version metadata ;; List of enums types metadata ;; List of retained types metadata ;; List of subprograms metadata ;; List of global variables } """ accept_optional_data = True type_descriptors = [ desc("tag", get_i32), desc("unused", get_i32), # Positional argument list starts here: desc("langid", get_i32), desc("source_filename", get_mdstr), desc("source_filedir", get_mdstr), desc("producer", get_mdstr), desc("is_main", get_i1, default=False), desc("is_optimized", get_i1, default=False), desc("compile_flags", get_mdstr, default=""), desc("runtime_version", get_i32, default=0), desc("enum_types", get_mdlist, default=empty), desc("retained_types", get_mdlist, default=empty), desc("subprograms", get_mdlist, default=empty), desc("global_vars", get_mdlist, default=empty), ] def __init__(self, *args, **kwargs): super(CompileUnitDescriptor, self).__init__( _dwarf.DW_TAG_compile_unit + _dwarf.LLVMDebugVersion, # tag 0, # unused *args, **kwargs) def define(self, llvm_module): """ Define this debug descriptor as named debug metadata for the module. """ md = self.get_metadata(llvm_module) llvm.core.MetaData.add_named_operand(llvm_module, "llvm.dbg.cu", md) class FileDescriptor(DebugInfoDescriptor): """ !0 = metadata !{ i32, ;; Tag = 41 + LLVMDebugVersion (DW_TAG_file_type) metadata, ;; Source file name metadata, ;; Source file directory (includes trailing slash) metadata ;; Unused } """ type_descriptors = [ desc("tag", get_i32), # Positional argument list starts here: desc("source_filename", get_mdstr), desc("source_filedir", get_mdstr), desc("compile_unit", get_md, default=null), ] def __init__(self, *args, **kwargs): super(FileDescriptor, self).__init__( _dwarf.DW_TAG_file_type + _dwarf.LLVMDebugVersion, # Tag *args, **kwargs) @classmethod def from_compileunit(cls, compile_unit_descriptor): return cls(compile_unit_descriptor.operands_dict["source_filename"], compile_unit_descriptor.operands_dict["source_filedir"], compile_unit_descriptor) class SubprogramDescriptor(DebugInfoDescriptor): """ !2 = metadata !{ i32, ;; Tag = 46 + LLVMDebugVersion (DW_TAG_subprogram) i32, ;; Unused field. metadata, ;; Reference to context descriptor metadata, ;; Name metadata, ;; Display name (fully qualified C++ name) metadata, ;; MIPS linkage name (for C++) metadata, ;; Reference to file where defined i32, ;; Line number where defined metadata, ;; Reference to type descriptor i1, ;; True if the global is local to compile unit (static) i1, ;; True if the global is defined in the compile unit (not extern) i32, ;; Line number where the scope of the subprogram begins i32, ;; Virtuality, e.g. dwarf::DW_VIRTUALITY__virtual i32, ;; Index into a virtual function metadata, ;; indicates which base type contains the vtable pointer for the ;; derived class i32, ;; Flags - Artifical, Private, Protected, Explicit, Prototyped. i1, ;; isOptimized Function * , ;; Pointer to LLVM function metadata, ;; Lists function template parameters metadata, ;; Function declaration descriptor metadata ;; List of function variables } """ accept_optional_data = True type_descriptors = [ desc("tag", get_i32), desc("unused", get_i32), # Positional argument list starts here: desc("file_desc", get_md), desc("name", get_mdstr), desc("display_name", get_mdstr), desc("mips_linkage_name", get_mdstr), desc("source_file_ref", get_md), desc("line_number", get_i32), desc("signature", get_md), desc("is_local", get_i1, default=False), desc("is_definition", get_i1, default=True), desc("virtual_attribute", get_i32, default=0), desc("virtual_index", get_i32, default=0), desc("virttab", get_i32, default=0), desc("flags", get_i32, default=0), desc("is_optimized", get_i1, default=False), desc("llvm_func", get_lfunc), # Template params # Func decl # Func vars ] def __init__(self, *args, **kwargs): super(SubprogramDescriptor, self).__init__( _dwarf.DW_TAG_subprogram + _dwarf.LLVMDebugVersion, # Tag 0, # Unused *args, **kwargs) class BlockDescriptor(DebugInfoDescriptor): """ !3 = metadata !{ i32, ;; Tag = 11 + LLVMDebugVersion (DW_TAG_lexical_block) metadata,;; Reference to context descriptor i32, ;; Line number i32 ;; Column number } """ type_descriptors = [ desc("tag", get_i32), # Positional argument list starts here: desc("context_descr", get_md), # FileDescr | BlockDescr | # SubprogDescr | ComputeUnit desc("line_number", get_i32), desc("col_number", get_i32), ] def __init__(self, *args, **kwargs): super(BlockDescriptor, self).__init__( _dwarf.DW_TAG_lexical_block + _dwarf.LLVMDebugVersion, *args, **kwargs) class PositionInfoDescriptor(DebugInfoDescriptor): """ line number, column number, scope, and original scope """ type_descriptors = [ desc("line_number", get_i32), desc("col_number", get_i32), desc("context_descr", get_md), # Scope of instruction # (FileDescr etc) desc("original_context_descr", get_md, # The original scope if inlined default=null), ]