Commit fe814e13 authored by Robert Flack's avatar Robert Flack Committed by Commit Bot

Implement GDB Xmethods for std::vector and std::unique_ptr.

This patch implements a helper for defining Xmethods simply and example
Xmethod implementations of useful functions on std::vector and
std::unique_ptr which are normally inlined and cannot be invoked.

This expediates debugging code, you don't have to remember which
internal data members represent the vector contents or unique_ptr
pointer value.

For example, before:

(rr) p ignore
$1 = std::__1::unique_ptr<base::AutoReset<bool>> = 0x30f6a6b22ac0 => {scoped_variable_ = 0x30f6a6987880, original_value_ = false}
(rr) p ignore.get()
Cannot evaluate function -- may be inlined
(rr) p *ignore
Could not find operator*.
(rr) p ignore->original_value_
Could not find operator->.

(rr) p layers
$2 = std::__1::vector (length=8, capacity=8) = {0x30f6a665a3a0}
(rr) p layers.size()
Cannot evaluate function -- may be inlined
(rr) p layers[0]
Could not find operator[].

After:

(rr) p ignore
$1 = std::__1::unique_ptr<base::AutoReset<bool>> = 0x30f6a6b22ac0 => {scoped_variable_ = 0x30f6a6987880, original_value_ = false}
(rr) p ignore.get()
$2 = (base::AutoReset<bool> *) 0x30f6a6b22ac0
(rr) p *ignore
$3 = {scoped_variable_ = 0x30f6a6987880, original_value_ = false}
(rr) p ignore->original_value_
$4 = false

(rr) p layers
$5 = std::__1::vector (length=8, capacity=8) = {0x30f6a665a3a0}
(rr) p layers.size()
$6 = 1
(rr) p layers[0]
$7 = (cc::FakePictureLayerImpl *) 0x30f6a665a3a0

Bug: None
Change-Id: Ia4b7495ca29568844d1e1c75ce6bcb3f1d8ba98e
Reviewed-on: https://chromium-review.googlesource.com/c/1302697
Commit-Queue: Robert Flack <flackr@chromium.org>
Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605328}
parent 469e7e48
...@@ -28,6 +28,11 @@ import gdb.printing ...@@ -28,6 +28,11 @@ import gdb.printing
import os import os
import sys import sys
sys.path.insert(0, os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'util'))
import class_methods
sys.path.insert(0, os.path.join( sys.path.insert(0, os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
'..', '..', 'third_party', 'blink', 'tools', 'gdb')) '..', '..', 'third_party', 'blink', 'tools', 'gdb'))
...@@ -375,3 +380,29 @@ pp_set.add_printer('content::RenderProcessHostImpl', ...@@ -375,3 +380,29 @@ pp_set.add_printer('content::RenderProcessHostImpl',
gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING) gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
"""Implementations of inlined libc++ std container functions."""
@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_']
@class_methods.Class('std::__1::unique_ptr', template_types=['T'])
class LibcppUniquePtr(object):
@class_methods.member_function('T*', 'get', [])
def get(obj):
return obj['__ptr_']['__value_']
@class_methods.member_function('T*', 'operator->', [])
def arrow(obj):
return obj['__ptr_']['__value_']
@class_methods.member_function('T&', 'operator*', [])
def dereference(obj):
return obj['__ptr_']['__value_'].dereference()
# 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 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment