SERVER-26634 GDB Pretty-Printers and Commands

This commit is contained in:
Mark Benvenuto 2016-12-09 17:57:35 -05:00
parent 93fc176c5e
commit 586ac20773
2 changed files with 73 additions and 67 deletions

View File

@ -1,4 +1,3 @@
# Print the full stack trace on python exceptions to aid debugging
set python print-stack full

View File

@ -25,6 +25,8 @@ def get_unique_ptr(obj):
# Pretty-Printers
#
###################################################################################################
class StatusPrinter(object):
"""Pretty-printer for mongo::Status"""
OK = 0 # ErrorCodes::OK
@ -33,10 +35,10 @@ class StatusPrinter(object):
self.val = val
def to_string(self):
code = self.code()
if code == StatusPrinter.OK:
if not self.val['_error']:
return 'Status::OK()'
code = self.val['_error']['code']
# Remove the mongo::ErrorCodes:: prefix. Does nothing if not a real ErrorCode.
code = str(code).split('::')[-1]
@ -44,47 +46,37 @@ class StatusPrinter(object):
location = info['location']
reason = info['reason']
if location:
return 'Status(%s, %s, %s)'%(code, reason, location)
return 'Status(%s, %s, %s)' % (code, reason, location)
else:
return 'Status(%s, %s)'%(code, reason)
def code(self):
if not self.val['_error']:
return StatusPrinter.OK
return self.val['_error'].dereference()['code']
return 'Status(%s, %s)' % (code, reason)
class StatusWithPrinter:
"""Pretty-printer for mongo::StatusWith<>"""
OK = 0 # ErrorCodes::OK
def __init__(self, val):
self.val = val
def to_string(self):
code = self.code()
if code == StatusPrinter.OK:
return 'StatusWith(%s, %s)'%(code, self.val['_t'])
if not self.val['_status']['_error']:
return 'StatusWith(OK, %s)' % (self.val['_t'])
code = self.val['_status']['_error']['code']
# Remove the mongo::ErrorCodes:: prefix. Does nothing if not a real ErrorCode.
code = str(code).split('::')[-1]
info = self.val['_status']['_error'].dereference()
location = info['_status']['location']
reason = info['_status']['reason']
location = info['location']
reason = info['reason']
if location:
return 'StatusWith(%s, %s, %s)'%(code, reason, location)
return 'StatusWith(%s, %s, %s)' % (code, reason, location)
else:
return 'StatusWith(%s, %s)'%(code, reason)
def code(self):
if not self.val['_status']['_error']:
return StatusWithPrinter.OK
return self.val['_status'].dereference()['code']
return 'StatusWith(%s, %s)' % (code, reason)
class StringDataPrinter:
"""Pretty-printer for mongo::StringData"""
def __init__(self, val):
self.val = val
@ -96,11 +88,12 @@ class StringDataPrinter:
if size == -1:
return self.val['_data'].lazy_string()
else:
return self.val['_data'].lazy_string(length = size)
return self.val['_data'].lazy_string(length=size)
class BSONObjPrinter:
"""Pretty-printer for mongo::BSONObj"""
def __init__(self, val):
self.val = val
self.ptr = self.val['_objdata'].cast(gdb.lookup_type('void').pointer())
@ -110,6 +103,9 @@ class BSONObjPrinter:
return 'map'
def children(self):
if not bson:
return
inferior = gdb.selected_inferior()
buf = bytes(inferior.read_memory(self.ptr, self.size))
options = CodecOptions(document_class=collections.OrderedDict)
@ -123,14 +119,13 @@ class BSONObjPrinter:
ownership = "owned" if self.val['_ownedBuffer']['_buffer']['_holder']['px'] else "unowned"
size = self.size
if size < 5 or size > 17*1024*1024:
# TODO: print invalid sizes in hex as they may be sentinel bytes.
if size < 5 or size > 17 * 1024 * 1024:
size = hex(size)
if size == 5:
return "%s empty BSONObj @ %s"%(ownership, self.ptr)
return "%s empty BSONObj @ %s" % (ownership, self.ptr)
else:
return "%s BSONObj %s bytes @ %s"%(ownership, size, self.ptr)
return "%s BSONObj %s bytes @ %s" % (ownership, size, self.ptr)
class UnorderedFastKeyTablePrinter:
@ -144,12 +139,12 @@ class UnorderedFastKeyTablePrinter:
valueType = gdb.lookup_type(valueTypeName).target()
self.valueTypePtr = valueType.pointer()
def display_hint (self):
def display_hint(self):
return 'map'
def to_string(self):
return "UnorderedFastKeyTablePrinter<%s> with %s elems " % (
self.val.type.template_argument(0), self.val["_size"])
self.val.type.template_argument(0), self.val["_size"])
def children(self):
cap = self.val["_area"]["_hashMask"] + 1
@ -182,17 +177,16 @@ class DecorablePrinter:
self.start = decl_vector["_M_impl"]["_M_start"]
finish = decl_vector["_M_impl"]["_M_finish"]
decinfo_t = gdb.lookup_type('mongo::DecorationRegistry::DecorationInfo')
self.count = int( (int(finish) - int(self.start)) / decinfo_t.sizeof )
self.count = int((int(finish) - int(self.start)) / decinfo_t.sizeof)
def display_hint (self):
def display_hint(self):
return 'map'
def to_string(self):
return "Decorable<%s> with %s elems " % (self.val.type.template_argument(0),
self.count)
self.count)
def children(self):
decorationData = get_unique_ptr(self.val["_decorations"]["_decorationData"])
for index in range(self.count):
@ -216,13 +210,13 @@ class DecorablePrinter:
type_t = gdb.lookup_type(type_name)
obj = decorationData[dindex].cast(type_t)
yield ('key', type_name)
yield ('value', obj.address)
yield ('key', "%d:%s:%s" % (index, obj.address, type_name))
yield ('value', obj)
def find_match_brackets(search, opening='<', closing='>'):
"""Returns the index of the closing bracket that matches the first opening bracket.
Returns -1 if no last matching bracket is found, i.e. not a a template.
Returns -1 if no last matching bracket is found, i.e. not a template.
Example:
'Foo<T>::iterator<U>''
@ -232,41 +226,43 @@ def find_match_brackets(search, opening='<', closing='>'):
if index == -1:
return -1
index = index + 1
start = index + 1
count = 1
str_len = len(search)
while index < str_len and count > 0:
for index in range(start, str_len):
c = search[index]
index = index + 1
if c == opening:
count = count + 1
count += 1
elif c == closing:
count = count - 1
count -= 1
if count == 0:
return index - 1
return index
return -1
class MongoSubPrettyPrinter(gdb.printing.SubPrettyPrinter):
"""Sub pretty printer managed by the pretty-printer collection"""
def __init__(self, name, prefix, printer):
def __init__(self, name, prefix, is_template, printer):
super(MongoSubPrettyPrinter, self).__init__(name)
self.prefix = prefix
self.printer = printer
self.is_template = is_template
class MongoPrettyPrinterCollection(gdb.printing.PrettyPrinter):
"""MongoDB-specific printer printer collection that ignores subtypes.
It will match 'HashTable<T> but not 'HashTable<T>::iterator' when asked for 'HashTable'
It will match 'HashTable<T> but not 'HashTable<T>::iterator' when asked for 'HashTable'.
"""
def __init__(self):
super(MongoPrettyPrinterCollection, self).__init__("mongo", [])
def add(self, name, prefix, printer):
self.subprinters.append(MongoSubPrettyPrinter(name, prefix, printer))
def add(self, name, prefix, is_template, printer):
self.subprinters.append(MongoSubPrettyPrinter(name, prefix, is_template, printer))
def __call__(self, val):
@ -283,7 +279,9 @@ class MongoPrettyPrinterCollection(gdb.printing.PrettyPrinter):
# We do not want HashTable<T>::iterator as an example, just HashTable<T>
if index == -1 or index + 1 == len(lookup_tag):
for printer in self.subprinters:
if printer.enabled and lookup_tag.find(printer.prefix) == 0:
if printer.enabled and (
(printer.is_template and lookup_tag.find(printer.prefix) == 0) or
(not printer.is_template and lookup_tag == printer.prefix)):
return printer.printer(val)
return None
@ -291,13 +289,12 @@ class MongoPrettyPrinterCollection(gdb.printing.PrettyPrinter):
def build_pretty_printer():
pp = MongoPrettyPrinterCollection()
if bson is not None:
pp.add('BSONObj', 'mongo::BSONObj', BSONObjPrinter)
pp.add('Decorable', 'mongo::Decorable', DecorablePrinter)
pp.add('Status', 'mongo::Status', StatusPrinter)
pp.add('StatusWith', 'mongo::StatusWith', StatusWithPrinter)
pp.add('StringData', 'mongo::StringData', StringDataPrinter)
pp.add('UnorderedFastKeyTable', 'mongo::UnorderedFastKeyTable', UnorderedFastKeyTablePrinter)
pp.add('BSONObj', 'mongo::BSONObj', False, BSONObjPrinter)
pp.add('Decorable', 'mongo::Decorable', True, DecorablePrinter)
pp.add('Status', 'mongo::Status', False, StatusPrinter)
pp.add('StatusWith', 'mongo::StatusWith', True, StatusWithPrinter)
pp.add('StringData', 'mongo::StringData', False, StringDataPrinter)
pp.add('UnorderedFastKeyTable', 'mongo::UnorderedFastKeyTable', True, UnorderedFastKeyTablePrinter)
return pp
###################################################################################################
@ -308,6 +305,7 @@ def build_pretty_printer():
# Dictionary of commands so we can write a help function that describes the MongoDB commands.
mongo_commands = {}
def register_mongo_command(obj, name, command_class):
"""Register a command with no completer as a mongo command"""
global mongo_commands
@ -315,15 +313,19 @@ def register_mongo_command(obj, name, command_class):
mongo_commands[name] = obj.__doc__
class DumpGlobalServiceContext(gdb.Command):
"""Dump the Global Service Context"""
def __init__(self):
register_mongo_command(self, "mongodb-services", gdb.COMMAND_DATA)
register_mongo_command(self, "mongodb-service-context", gdb.COMMAND_DATA)
def invoke(self, arg, _from_tty):
gdb.execute("print *('mongo::(anonymous namespace)::globalServiceContext')")
# Register command
DumpGlobalServiceContext()
class MongoDBAnalyze(gdb.Command):
"""Analyze MongoDB process"""
@ -347,10 +349,17 @@ class MongoDBAnalyze(gdb.Command):
def analyze_mongod(self):
"""GDB in-process python supplement"""
# Call into mongod, and dump the state of lock manager
# Note that output will go to mongod's standard output, not the debugger output window
gdb.execute("call ('mongo::(anonymous namespace)::globalLockManager').dump()",
from_tty=False, to_string=False)
try:
# Call into mongod, and dump the state of lock manager
# Note that output will go to mongod's standard output, not the debugger output window
gdb.execute("call ('mongo::(anonymous namespace)::globalLockManager').dump()",
from_tty=False, to_string=False)
except gdb.Error as gdberr:
print("Ignoring error '%s'" % str(gdberr))
# Register command
MongoDBAnalyze()
class MongoDBHelp(gdb.Command):
@ -364,9 +373,7 @@ class MongoDBHelp(gdb.Command):
for key in mongo_commands:
print("%s - %s" % (key, mongo_commands[key]))
# Initialize Commands
DumpGlobalServiceContext()
MongoDBAnalyze()
# Register command
MongoDBHelp()
###################################################################################################
@ -377,8 +384,8 @@ MongoDBHelp()
# Register pretty-printers, replace existing mongo printers
gdb.printing.register_pretty_printer(
gdb.current_objfile(),
build_pretty_printer(),
True)
gdb.current_objfile(),
build_pretty_printer(),
True)
print("MongoDB GDB pretty-printers and commands loaded, run 'mongodb-help' for list of commands")