@@ -655,6 +655,14 @@ def first_tid(stack):
655
655
class MongoDBJavaScriptStack (gdb .Command ):
656
656
"""Print the JavaScript stack from a MongoDB process."""
657
657
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
+
658
666
def __init__ (self ):
659
667
"""Initialize MongoDBJavaScriptStack."""
660
668
RegisterMongoCommand .register (self , "mongodb-javascript-stack" , gdb .COMMAND_STATUS )
@@ -669,6 +677,24 @@ def invoke(self, arg, _from_tty): # pylint: disable=unused-argument
669
677
else :
670
678
print ("No JavaScript stack print done for: %s" % (main_binary_name ))
671
679
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
+
672
698
@staticmethod
673
699
def javascript_stack ():
674
700
"""GDB in-process python supplement."""
@@ -690,13 +716,29 @@ def javascript_stack():
690
716
continue
691
717
692
718
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
+
700
742
except gdb .error as err :
701
743
print ("Ignoring GDB error '%s' in javascript_stack" % str (err ))
702
744
continue
0 commit comments