SERVER-26634 GDB Pretty-Printers and Commands
This commit is contained in:
parent
93fc176c5e
commit
586ac20773
1
.gdbinit
1
.gdbinit
@ -1,4 +1,3 @@
|
||||
|
||||
# Print the full stack trace on python exceptions to aid debugging
|
||||
set python print-stack full
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user