Merge pull request #47 from llvmpy/debuginfo

Debuginfo
This commit is contained in:
Siu Kwan Lam 2013-01-23 12:09:16 -08:00
commit 1ccda1ca5c
5 changed files with 695 additions and 1 deletions

86
llvm/_dwarf.cpp Normal file
View file

@ -0,0 +1,86 @@
#include <Python.h>
#include <llvm/Support/Dwarf.h>
namespace llvmpy {
namespace dwarf {
using namespace llvm::dwarf;
const char *constant_names[] = {
"LLVMDebugVersion",
#define define(x) #x,
#include "_dwarf.h"
#undef define
};
int constant_values[] = {
llvm::LLVMDebugVersion,
#define define(x) x,
#include "_dwarf.h"
#undef define
};
enum enum_values {
__llvmpy_LLVMDebugVersion,
#define define(x) __llvmpy_##x,
#include "_dwarf.h"
#undef define
NVALUES /* Get the number of constants */
};
} // End namespace dwarf
int
set_dwarf_constants(PyObject *module)
{
int i;
for (i = 0; i < dwarf::NVALUES; i++) {
if (PyModule_AddIntConstant(module, dwarf::constant_names[i],
dwarf::constant_values[i]) > 0)
return -1;
}
return 0;
}
} // End namespace llvmpy
#if (PY_MAJOR_VERSION >= 3)
struct PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"_dwarf",
NULL,
-1,
NULL, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#define INITERROR return NULL
PyObject *PyInit__dwarf(void)
#else
#define INITERROR return
PyMODINIT_FUNC init_dwarf(void)
#endif
{
#if PY_MAJOR_VERSION >= 3
PyObject *module = PyModule_Create( &module_def );
#else
PyObject *module = Py_InitModule("_dwarf", NULL);
#endif
if (module == NULL)
INITERROR;
if (llvmpy::set_dwarf_constants(module) < 0)
INITERROR;
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}

101
llvm/_dwarf.h Normal file
View file

@ -0,0 +1,101 @@
/* Dwarf constant wrapping. See include/llvm/Support/Dwarf.h */
define(DWARF_VERSION)
/* Dwarf tags */
define(DW_TAG_array_type)
define(DW_TAG_class_type)
define(DW_TAG_entry_point)
define(DW_TAG_enumeration_type)
define(DW_TAG_formal_parameter)
define(DW_TAG_imported_declaration)
define(DW_TAG_label)
define(DW_TAG_lexical_block)
define(DW_TAG_member)
define(DW_TAG_pointer_type)
define(DW_TAG_reference_type)
define(DW_TAG_compile_unit)
define(DW_TAG_string_type)
define(DW_TAG_structure_type)
define(DW_TAG_subroutine_type)
define(DW_TAG_typedef)
define(DW_TAG_union_type)
define(DW_TAG_unspecified_parameters)
define(DW_TAG_variant)
define(DW_TAG_common_block)
define(DW_TAG_common_inclusion)
define(DW_TAG_inheritance)
define(DW_TAG_inlined_subroutine)
define(DW_TAG_module)
define(DW_TAG_ptr_to_member_type)
define(DW_TAG_set_type)
define(DW_TAG_subrange_type)
define(DW_TAG_with_stmt)
define(DW_TAG_access_declaration)
define(DW_TAG_base_type)
define(DW_TAG_catch_block)
define(DW_TAG_const_type)
define(DW_TAG_constant)
define(DW_TAG_enumerator)
define(DW_TAG_file_type)
define(DW_TAG_friend)
define(DW_TAG_namelist)
define(DW_TAG_namelist_item)
define(DW_TAG_packed_type)
define(DW_TAG_subprogram)
define(DW_TAG_template_type_parameter)
define(DW_TAG_template_value_parameter)
define(DW_TAG_thrown_type)
define(DW_TAG_try_block)
define(DW_TAG_variant_part)
define(DW_TAG_variable)
define(DW_TAG_volatile_type)
define(DW_TAG_dwarf_procedure)
define(DW_TAG_restrict_type)
define(DW_TAG_interface_type)
define(DW_TAG_namespace)
define(DW_TAG_imported_module)
define(DW_TAG_unspecified_type)
define(DW_TAG_partial_unit)
define(DW_TAG_imported_unit)
define(DW_TAG_condition)
define(DW_TAG_shared_type)
define(DW_TAG_type_unit)
define(DW_TAG_rvalue_reference_type)
define(DW_TAG_template_alias)
define(DW_TAG_MIPS_loop)
define(DW_TAG_format_label)
define(DW_TAG_function_template)
define(DW_TAG_class_template)
define(DW_TAG_GNU_template_template_param)
define(DW_TAG_GNU_template_parameter_pack)
define(DW_TAG_GNU_formal_parameter_pack)
define(DW_TAG_lo_user)
define(DW_TAG_APPLE_property)
define(DW_TAG_hi_user)
/* Dwarf language constants */
define(DW_LANG_C89)
define(DW_LANG_C)
define(DW_LANG_Ada83)
define(DW_LANG_C_plus_plus)
define(DW_LANG_Cobol74)
define(DW_LANG_Cobol85)
define(DW_LANG_Fortran77)
define(DW_LANG_Fortran90)
define(DW_LANG_Pascal83)
define(DW_LANG_Modula2)
define(DW_LANG_Java)
define(DW_LANG_C99)
define(DW_LANG_Ada95)
define(DW_LANG_Fortran95)
define(DW_LANG_PLI)
define(DW_LANG_ObjC)
define(DW_LANG_ObjC_plus_plus)
define(DW_LANG_UPC)
define(DW_LANG_D)
define(DW_LANG_Python)
define(DW_LANG_lo_user)
define(DW_LANG_Mips_Assembler)
define(DW_LANG_hi_user)

398
llvm/debuginfo.py Normal file
View file

@ -0,0 +1,398 @@
"""
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),
]

View file

@ -116,7 +116,12 @@ kwds = dict(ext_modules = [Extension(
library_dirs = [libdir],
libraries = libs_core,
extra_objects = objs_core,
extra_link_args = extra_link_args)])
extra_link_args = extra_link_args),
Extension(name='llvm._dwarf',
sources=['llvm/_dwarf.cpp'],
include_dirs=[incdir])
])
def run_2to3():
import lib2to3.refactor

104
test/test_debuginfo.py Normal file
View file

@ -0,0 +1,104 @@
import os
import unittest
from llvm.core import *
from llvm import _dwarf, debuginfo
class TestDebugInfo(unittest.TestCase):
def test_dwarf_constants(self):
dwarf_constants = vars(_dwarf)
# Version numbers
self.assertIn("LLVMDebugVersion", dwarf_constants)
self.assertIn("DWARF_VERSION", dwarf_constants)
# Tags
self.assertIn("DW_TAG_compile_unit", dwarf_constants)
# Language identifiers
self.assertIn("DW_LANG_Python", dwarf_constants)
self.assertIn("DW_LANG_C89", dwarf_constants)
self.assertIn("DW_LANG_C99", dwarf_constants)
# print sorted([constname for constname in dwarf_constants
# if constname.startswith("DW_LANG_")])
def test_debug_info_compile_unit(self):
mod = Module.new('test_debug_info')
fty = Type.function(Type.float(), [Type.float()])
square = mod.add_function(fty, 'square')
bb = square.append_basic_block('entry')
bldr = Builder.new(bb)
source_filename = "test_debug_info.py"
source_filedir = os.path.expanduser("~")
# Debug info for our file
filedesc = debuginfo.FileDescriptor(source_filename, source_filedir)
# Debug info for our function
subprogram = debuginfo.SubprogramDescriptor(
filedesc,
"some_function",
"some_function",
"some_function",
filedesc,
1, # line number
debuginfo.empty, # Type descriptor
llvm_func=square,
)
subprograms = debuginfo.MDList([subprogram])
# Debug info for our basic blocks
blockdescr1 = debuginfo.BlockDescriptor(subprogram, 2, 4) # lineno, col
blockdescr2 = debuginfo.BlockDescriptor(blockdescr1, 3, 4) # lineno, col
# Debug info for our instructions
posinfo1 = debuginfo.PositionInfoDescriptor(2, 4, blockdescr1)
posinfo2 = debuginfo.PositionInfoDescriptor(2, 4, blockdescr2)
# Debug info for our module
compile_unit = debuginfo.CompileUnitDescriptor(
# DW_LANG_Python segfaults:
# llvm/ADT/StringRef.h:79: llvm::StringRef::StringRef(const char*):
# Assertion `Str && "StringRef cannot be built from a NULL argument"'
# _dwarf.DW_LANG_Python,
_dwarf.DW_LANG_C89,
source_filename,
source_filedir,
"my_cool_compiler",
subprograms=subprograms,
)
# Define our module debug data
compile_unit.define(mod)
# Build some instructions
value = square.args[0]
result = bldr.fmul(value, value)
ret = bldr.ret(result)
# Annotate instructions with source position
result.set_metadata("dbg", posinfo1.get_metadata(mod))
ret.set_metadata("dbg", posinfo2.get_metadata(mod))
# ... Aaaand, test...
# print mod
modstr = str(mod)
# Test compile unit
self.assertIn("my_cool_compiler", modstr)
self.assertIn(source_filename, modstr)
self.assertIn(source_filedir, modstr)
self.assertIn("my_cool_compiler", modstr)
# Test subprogram
self.assertIn("some_function", modstr)
self.assertIn("float (float)* @square", modstr)
if __name__ == '__main__':
# TestDebugInfo("test_dwarf_constants").debug()
# TestDebugInfo("test_debug_info_compile_unit").debug()
unittest.main()