Skip to content

Latest commit

 

History

History
279 lines (231 loc) · 10.3 KB

jstack-how-it-works.md

File metadata and controls

279 lines (231 loc) · 10.3 KB

How jstack works

jstack program main class

The jstack program attaches to running JVM using pid, connecto UNIX socket created by Attach Listener Thread of the running JVM to send command threaddump

    #sun/tools/jstack/JStack.java:

    public class JStack {
        public static void main(String[] args) throws Exception {
            if (args.length == 0) {
                usage(); // no arguments
            }
    
    ...
            // mixed stack implies SA tool
            if (mixed) {
                useSA = true;
            }
    
    ...
    
            if (useSA) {
                // parameters (<pid> or <exe> <core>
                String params[] = new String[paramCount];
                for (int i=optionCount; i<args.length; i++ ){
                    params[i-optionCount] = args[i];
                }
                runJStackTool(mixed, locks, params);
            } else {
                // pass -l to thread dump operation to get extra lock info
                String pid = args[optionCount];
                String params[];
                if (locks) {
                    params = new String[] { "-l" };
                } else {
                    params = new String[0];
                }
                runThreadDump(pid, params);
            }
    

    #sun/tools/jstack/JStack.java:
      private static void runThreadDump(String pid, String args[]) throws Exception {
        VirtualMachine vm = null;
        try {
            vm = VirtualMachine.attach(pid);
        } catch (Exception x) {
            String msg = x.getMessage();
            if (msg != null) {
                System.err.println(pid + ": " + msg);
            } else {
                x.printStackTrace();
            }
            if ((x instanceof AttachNotSupportedException) &&
                (loadSAClass() != null)) {
                System.err.println("The -F option can be used when the target " +
                    "process is not responding");
            }
            System.exit(1);
        }

        // Cast to HotSpotVirtualMachine as this is implementation specific
        // method.
        InputStream in = ((HotSpotVirtualMachine)vm).remoteDataDump((Object[])args);

        // read to EOF and just print output
        byte b[] = new byte[256];
        int n;
        do {
            n = in.read(b);
            if (n > 0) {
                String s = new String(b, 0, n, "UTF-8");
                System.out.print(s);
            }
        } while (n > 0);
        in.close();
        vm.detach();

    #sun/tools/jstack/JStack.java:

        private static void runJStackTool(boolean mixed, boolean locks, String args[]) throws Exception {
            Class<?> cl = loadSAClass();
            if (cl == null) {
                usage();            // SA not available
            }
    
            // JStack tool also takes -m and -l arguments
            if (mixed) {
                args = prepend("-m", args);
            }
            if (locks) {
                args = prepend("-l", args);
            }
    
            Class[] argTypes = { String[].class };
            Method m = cl.getDeclaredMethod("main", argTypes);
    
            Object[] invokeArgs = { args };
            m.invoke(null, invokeArgs);
        }
    

    #sun/tools/jstack/JStack.java:
        private static Class loadSAClass() {
            //
            // Attempt to load JStack class - we specify the system class
            // loader so as to cater for development environments where
            // this class is on the boot class path but sa-jdi.jar is on
            // the system class path. Once the JDK is deployed then both
            // tools.jar and sa-jdi.jar are on the system class path.
            //
            try {
                return Class.forName("sun.jvm.hotspot.tools.JStack", true,
                                     ClassLoader.getSystemClassLoader());
            } catch (Exception x)  { }
            return null;
        }
    

    #sun/tools/attach/HotSpotVirtualMachine.java:

        // Remote ctrl-break. The output of the ctrl-break actions can
        // be read from the input stream.
        public InputStream remoteDataDump(Object ... args) throws IOException {
            return executeCommand("threaddump", args);
        }

      #sun/tools/attach/HotSpotVirtualMachine.java:
        private InputStream executeCommand(String cmd, Object ... args) throws IOException {
            try {
                return execute(cmd, args);
            } catch (AgentLoadException x) {
                throw new InternalError("Should not get here");
            }
        }

The execution of threaddump command inside JVM

The Attatch Listener Thread that read an incomming operation an dispatch it to appropriate function

    #share/vm/services/attachListener.cpp
    static void attach_listener_thread_entry(JavaThread* thread, TRAPS) {
    ...
      for (;;) {
        AttachOperation* op = AttachListener::dequeue();
    
       if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) {
          AttachListener::detachall();
        } else {
          // find the function to dispatch too
          AttachOperationFunctionInfo* info = NULL;
          for (int i=0; funcs[i].name != NULL; i++) {
            const char* name = funcs[i].name;
            assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max");
            if (strcmp(op->name(), name) == 0) {
              info = &(funcs[i]);
              break;
            }
          }
     ... 
          if (info != NULL) {
            // dispatch to the function that implements this operation
            res = (info->func)(op, &st);
          } else {
            st.print("Operation %s not recognized!", op->name());
            res = JNI_ERR;
          }
        }
    
        // operation complete - send result and output to client
        op->complete(res, &st);

Table of functions that implements each operation

    #share/vm/services/attachListener.cpp
    
    // names must be of length <= AttachOperation::name_length_max
    static AttachOperationFunctionInfo funcs[] = {
      { "agentProperties",  get_agent_properties },
      { "datadump",         data_dump },
    #ifndef SERVICES_KERNEL
      { "dumpheap",         dump_heap },
    #endif  // SERVICES_KERNEL
      { "load",             JvmtiExport::load_agent_library },
      { "properties",       get_system_properties },
      { "threaddump",       thread_dump },
      { "inspectheap",      heap_inspection },
      { "setflag",          set_flag },
      { "printflag",        print_flag },
      { NULL,               NULL }
    };

Thread Dump funcion

the thread_dump function create intented VM operations and execute it in the context of VM thread

    #share/vm/services/attachListener.cpp
    static jint thread_dump(AttachOperation* op, outputStream* out) {
      bool print_concurrent_locks = false;
      if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) {
        print_concurrent_locks = true;
      }
    
      // thread stacks
      VM_PrintThreads op1(out, print_concurrent_locks);
      VMThread::execute(&op1);
    
      // JNI global handles
      VM_PrintJNI op2(out);
      VMThread::execute(&op2);
    
      // Deadlock detection
      VM_FindDeadlocks op3(out);
      VMThread::execute(&op3);
    
      return JNI_OK;
    }

There are bundle of VM_operation to process different administrative aspects of JVM such as thread, gc, lock, they are defined in

    #share/vm/runtime/vm_operations.hpp

    #define VM_OPS_DO(template)                       \
      template(Dummy)                                 \
      template(ThreadStop)                            \
      template(ThreadDump)                            \
      template(PrintThreads)                          \
      template(FindDeadlocks)                         \
      template(ForceSafepoint)                        \
      template(ForceAsyncSafepoint)                   \
      template(Deoptimize)                            \
      template(DeoptimizeFrame)                       \
      template(DeoptimizeAll)                         \
      template(ZombieAll)                             \
      template(HandleFullCodeCache)                   \
      template(Verify)                                \

    class VM_PrintThreads: public VM_Operation {
     private:
      outputStream* _out;
      bool _print_concurrent_locks;
     public:
      VM_PrintThreads() { _out = tty; _print_concurrent_locks = PrintConcurrentLocks; }
      VM_PrintThreads(outputStream* out, bool print_concurrent_locks)  { _out = out; _print_concurrent_locks = print_concurrent_locks; }
      VMOp_Type type() const  {  return VMOp_PrintThreads; }
      void doit();
      bool doit_prologue();
      void doit_epilogue();
    };

The operation is add to queue of the VM Thread, a thread of JVM dedicated for execution of administrative operation

    #share/vm/runtime/vmThread.cpp

    void VMThread::execute(VM_Operation* op) {
      Thread* t = Thread::current();
    
      if (!t->is_VM_thread()) {
    ...
        // New request from Java thread, evaluate prologue
        if (!op->doit_prologue()) {
          return;   // op was cancelled
        }
    ...
          VMOperationQueue_lock->lock_without_safepoint_check();
          bool ok = _vm_queue->add(op);
    ... 
        if (execute_epilog) {
          op->doit_epilogue();
        }
      } else {
    ...
    }