1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
# Copyright (c) 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper library for defining XMethod implementations on C++ classes.
Include this library and then define python implementations of C++ methods
using the Class and member_function decorator functions.
"""
import gdb
import gdb.xmethod
import operator
import re
class MemberFunction(object):
def __init__(self, return_type, name, arguments, wrapped_function):
self.return_type = return_type
self.name = name
self.arguments = arguments
self.function_ = wrapped_function
def __call__(self, *args):
self.function_(*args)
def member_function(return_type, name, arguments):
"""Decorate a member function.
See Class decorator for example usage within a class.
Args:
return_type: The return type of the function (e.g. 'int')
name: The function name (e.g. 'sum')
arguments: The argument types for this function (e.g. ['int', 'int'])
Each type can be a string (e.g. 'int', 'std::string', 'T*') or a
function which constructs the return type. See CreateTypeResolver
for details about type resolution.
"""
def DefineMember(fn):
return MemberFunction(return_type, name, arguments, fn)
return DefineMember
def Class(class_name, template_types):
"""Decorate a python class with its corresponding C++ type.
Args:
class_name: The canonical string identifier for the class (e.g. base::Foo)
template_types: An array of names for each templated type (e.g. ['K',
'V'])
Example:
As an example, the following is an implementation of size() and operator[]
on std::__1::vector, functions which are normally inlined and not
normally callable from gdb.
@class_methods.Class('std::__1::vector', template_types=['T'])
class LibcppVector(object):
@class_methods.member_function('T&', 'operator[]', ['int'])
def element(obj, i):
return obj['__begin_'][i]
@class_methods.member_function('size_t', 'size', [])
def size(obj):
return obj['__end_'] - obj['__begin_']
Note:
Note that functions are looked up by the function name, which means that
functions cannot currently have overloaded implementations for different
arguments.
"""
class MethodWorkerWrapper(gdb.xmethod.XMethod):
"""Wrapper of an XMethodWorker class as an XMethod."""
def __init__(self, name, worker_class):
super(MethodWorkerWrapper, self).__init__(name)
self.name = name
self.worker_class = worker_class
class ClassMatcher(gdb.xmethod.XMethodMatcher):
"""Matches member functions of one class template."""
def __init__(self, obj):
super(ClassMatcher, self).__init__(class_name)
# Constructs a regular expression to match this type.
self._class_regex = re.compile(
'^' + re.escape(class_name) +
('<.*>' if len(template_types) > 0 else '') + '$')
# Construct a dictionary and array of methods
self.dict = {}
self.methods = []
for name in dir(obj):
attr = getattr(obj, name)
if not isinstance(attr, MemberFunction):
continue
name = attr.name
return_type = CreateTypeResolver(attr.return_type)
arguments = [CreateTypeResolver(arg) for arg in
attr.arguments]
method = MethodWorkerWrapper(
attr.name,
CreateTemplatedMethodWorker(return_type,
arguments, attr.function_))
self.methods.append(method)
def match(self, class_type, method_name):
if not re.match(self._class_regex, class_type.tag):
return None
templates = [class_type.template_argument(i) for i in
range(len(template_types))]
return [method.worker_class(templates) for method in self.methods
if method.name == method_name and method.enabled]
def CreateTypeResolver(type_desc):
"""Creates a callback which resolves to the appropriate type when
invoked.
This is a helper to allow specifying simple types as strings when
writing function descriptions. For complex cases, a callback can be
passed which will be invoked when template instantiation is known.
Args:
type_desc: A callback generating the type or a string description of
the type to lookup. Supported types are classes in the
template_classes array (e.g. T) which will be looked up when those
templated classes are known, or globally visible type names (e.g.
int, base::Foo).
Types can be modified by appending a '*' or '&' to denote a
pointer or reference.
If a callback is used, the callback will be passed an array of the
instantiated template types.
Note:
This does not parse complex types such as std::vector<T>::iterator,
to refer to types like these you must currently write a callback
which constructs the appropriate type.
"""
if callable(type_desc):
return type_desc
if type_desc == 'void':
return lambda T: None
if type_desc[-1] == '&':
inner_resolver = CreateTypeResolver(type_desc[:-1])
return lambda template_types: inner_resolver(template_types).reference()
if type_desc[-1] == '*':
inner_resolver = CreateTypeResolver(type_desc[:-1])
return lambda template_types: inner_resolver(template_types).pointer()
try:
template_index = template_types.index(type_desc)
return operator.itemgetter(template_index)
except ValueError:
return lambda template_types: gdb.lookup_type(type_desc)
def CreateTemplatedMethodWorker(return_callback, args_callbacks,
method_callback):
class TemplatedMethodWorker(gdb.xmethod.XMethodWorker):
def __init__(self, templates):
super(TemplatedMethodWorker, self).__init__()
self._templates = templates
def get_arg_types(self):
return [cb(self._templates) for cb in args_callbacks]
def get_result_type(self, obj):
return return_callback(self._templates)
def __call__(self, *args):
return method_callback(*args)
return TemplatedMethodWorker
def DefineClass(obj):
matcher = ClassMatcher(obj)
gdb.xmethod.register_xmethod_matcher(None, matcher)
return matcher
return DefineClass
|