"""Add user-defined commands to MongoDB.""" import argparse import shlex def __lldb_init_module(debugger, *_args): """Register custom commands.""" debugger.HandleCommand( "command script add -o -f lldb_commands.PrintGlobalServiceContext mongodb-service-context") debugger.HandleCommand( "command script add -o -f lldb_commands.PrintGlobalServiceContext mongodb-dump-locks") debugger.HandleCommand( "command script add -o -f lldb_commands.BreakpointOnAssert mongodb-breakpoint-assert") debugger.HandleCommand( "command script add -o -f lldb_commands.MongoDBFindBreakpoint mongodb-find-breakpoint") debugger.HandleCommand("command script add -o -f lldb_commands.DumpGSC mongodb-gsc") debugger.HandleCommand("command alias mongodb-help help") ####################### # Command Definitions # ####################### def PrintGlobalServiceContext(debugger, *_args): # pylint: disable=invalid-name """Provide the mongodb-service-context command. Emulates the same convenience command available in GDB integrations to print the globalServiceContext. """ debugger.HandleCommand("print *globalServiceContext") def MongoDBDumpLocks(debugger, *_args): # pylint: disable=invalid-name """Dump locks in the mongod process.""" debugger.HandleCommand("call mongo::getGlobalLockManager()->dump()") def BreakpointOnAssert(debugger, command, _exec_ctx, _result, _internal_dict): # pylint: disable=invalid-name """Set a breakpoint on MongoDB uassert that throws the specified error code.""" arg_strs = shlex.split(command) parser = argparse.ArgumentParser(description='Set a breakpoint on a usassert code.') parser.add_argument('code', metavar='N', type=int, help='uassert code') args = parser.parse_args(arg_strs) debugger.HandleCommand( "breakpoint set -n mongo::uassertedWithLocation -c \"(int)status._error.px->code == %s\"" % args.code) def MongoDBFindBreakpoint(debugger, _command, exec_ctx, _result, _internal_dict): # pylint: disable=invalid-name """Find the thread that triggered a breakpoint from 'debugger.cpp'.""" process = exec_ctx.process print("Threads: %d" % (len(process.threads))) thread_num = 0 for thread_index, thread in enumerate(process.threads): frame_count = min(thread.num_frames, 10) for frame_index in range(frame_count): frame_str = thread.frames[frame_index].__str__() # Find the frame that has a call to `execCallback` the function `src/mongo/util/debugger.cpp` uses if "execCallback" in frame_str: thread_num = thread_index + 1 break if thread_num: break print("Switching thread to thread that hit breakpoint: %s" % (thread_num)) debugger.HandleCommand("thread select %d" % (thread_num)) def DumpGSC(_debugger, _command, exec_ctx, _result, _internal_dict): # pylint: disable=invalid-name """Dump the global service context as a hash table.""" gsc_list = exec_ctx.target.FindGlobalVariables("globalServiceContext", 1) print(gsc_list) gsc = gsc_list[0] decorations = gsc.GetChildMemberWithName("_decorations") registry = decorations.GetChildMemberWithName("_registry") decoration_info = registry.GetChildMemberWithName("_decorationInfo") decoration_data = decorations.GetChildMemberWithName("_decorationData").child[0] print(decoration_info.num_children) for child in range(decoration_info.num_children): di = decoration_info.children[child] constructor = di.GetChildMemberWithName("constructor").__str__() index = di.GetChildMemberWithName("descriptor").GetChildMemberWithName( "_index").GetValueAsUnsigned() type_name = constructor type_name = type_name[0:len(type_name) - 1] type_name = type_name[0:type_name.rindex(">")] type_name = type_name[type_name.index("constructAt<"):].replace("constructAt<", "") # If the type is a pointer type, strip the * at the end. if type_name.endswith('*'): type_name = type_name[0:len(type_name) - 1] type_name = type_name.rstrip() type_t = exec_ctx.target.FindTypes(type_name).GetTypeAtIndex(0) offset_ptr = decoration_data.GetChildAtIndex(index, False, True) value = offset_ptr.Cast(type_t) print(value)