@@ -655,6 +655,14 @@ def first_tid(stack):
655655class MongoDBJavaScriptStack (gdb .Command ):
656656 """Print the JavaScript stack from a MongoDB process."""
657657
658+ # Looking to test your changes to this? Really easy!
659+ # 1. install-core to build the mongo shell binary (mongo)
660+ # 2. launch it: ./path/to/bin/mongo --nodb
661+ # 3. in the shell, run: sleep(99999999999). (do not use --eval)
662+ # 4. ps ax | grep nodb to find the PID
663+ # 5. gdb -p <PID>.
664+ # 6. Run this command, mongodb-javascript-stack
665+
658666 def __init__ (self ):
659667 """Initialize MongoDBJavaScriptStack."""
660668 RegisterMongoCommand .register (self , "mongodb-javascript-stack" , gdb .COMMAND_STATUS )
@@ -669,6 +677,24 @@ def invoke(self, arg, _from_tty): # pylint: disable=unused-argument
669677 else :
670678 print ("No JavaScript stack print done for: %s" % (main_binary_name ))
671679
680+ @staticmethod
681+ def atomic_get_ptr (atomic_scope : gdb .Value ):
682+ """Fetch the underlying pointer from std::atomic."""
683+
684+ # Awkwardly, the gdb.Value type does not support a check like
685+ # `'_M_b' in atomic_scope`, so exceptions for flow control it is. :|
686+ try :
687+ # reach into std::atomic and grab the pointer. This is for libc++
688+ return atomic_scope ['_M_b' ]['_M_p' ]
689+ except gdb .error :
690+ # Worst case scenario: try and use .load(), but it's probably
691+ # inlined. parse_and_eval required because you can't call methods
692+ # in gdb on the Python API
693+ return gdb .parse_and_eval (
694+ f"((std::atomic<mongo::mozjs::MozJSImplScope*> *)({ atomic_scope .address } ))->load()" )
695+
696+ return None
697+
672698 @staticmethod
673699 def javascript_stack ():
674700 """GDB in-process python supplement."""
@@ -690,13 +716,29 @@ def javascript_stack():
690716 continue
691717
692718 try :
693- if gdb .parse_and_eval (
694- 'mongo::mozjs::kCurrentScope && mongo::mozjs::kCurrentScope->_inOp' ):
695- gdb .execute ('thread' , from_tty = False , to_string = False )
696- gdb .execute (
697- 'printf "%s\\ n", ' +
698- 'mongo::mozjs::kCurrentScope->buildStackString().c_str()' , from_tty = False ,
699- to_string = False )
719+ # The following block is roughly equivalent to this:
720+ # namespace mongo::mozjs {
721+ # std::atomic<MozJSImplScope*> kCurrentScope = ...;
722+ # }
723+ # if (!scope || scope->_inOp == 0) { continue; }
724+ # print(scope->buildStackString()->c_str());
725+ atomic_scope = gdb .parse_and_eval ("mongo::mozjs::kCurrentScope" )
726+ ptr = MongoDBJavaScriptStack .atomic_get_ptr (atomic_scope )
727+ if not ptr :
728+ continue
729+
730+ scope = ptr .dereference ()
731+ if scope ['_inOp' ] == 0 :
732+ continue
733+
734+ gdb .execute ('thread' , from_tty = False , to_string = False )
735+ # gdb continues to not support calling methods through Python,
736+ # so work around it by casting the raw ptr back to its type,
737+ # and calling the method through execute darkness
738+ gdb .execute (
739+ f'printf "%s\\ n", ((mongo::mozjs::MozJSImplScope*)({ ptr } ))->buildStackString().c_str()' ,
740+ from_tty = False , to_string = False )
741+
700742 except gdb .error as err :
701743 print ("Ignoring GDB error '%s' in javascript_stack" % str (err ))
702744 continue
0 commit comments