1d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe/*
2d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * Copyright (C) 2017 The Android Open Source Project
3d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe *
4d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * Licensed under the Apache License, Version 2.0 (the "License");
5d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * you may not use this file except in compliance with the License.
6d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * You may obtain a copy of the License at
7d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe *
8d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe *      http://www.apache.org/licenses/LICENSE-2.0
9d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe *
10d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * Unless required by applicable law or agreed to in writing, software
11d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * distributed under the License is distributed on an "AS IS" BASIS,
12d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * See the License for the specific language governing permissions and
14d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe * limitations under the License.
15d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe */
16d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
174665167ddc34008dfa78a2873685fe7a98772eabAndreas Gampepackage art;
184665167ddc34008dfa78a2873685fe7a98772eabAndreas Gampe
194471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampeimport java.util.ArrayList;
20d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampeimport java.util.Arrays;
21d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampeimport java.util.Comparator;
224471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampeimport java.util.Collections;
234471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampeimport java.util.Iterator;
244471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampeimport java.util.List;
25d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
264665167ddc34008dfa78a2873685fe7a98772eabAndreas Gampepublic class Test925 {
274665167ddc34008dfa78a2873685fe7a98772eabAndreas Gampe  public static void run() throws Exception {
28d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    doTest();
29d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  }
30d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
31d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static void doTest() throws Exception {
32d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Thread t1 = Thread.currentThread();
33d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    ThreadGroup curGroup = t1.getThreadGroup();
34d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
35d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    ThreadGroup rootGroup = curGroup;
36d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    while (rootGroup.getParent() != null) {
37d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      rootGroup = rootGroup.getParent();
38d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
39d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
40d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    ThreadGroup topGroups[] = getTopThreadGroups();
41d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) {
42d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      System.out.println(Arrays.toString(topGroups));
43d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      throw new RuntimeException("Unexpected topGroups");
44d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
45d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
46d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    printThreadGroupInfo(curGroup);
47d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    printThreadGroupInfo(rootGroup);
48d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
49d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */);
50d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
51d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    checkChildren(curGroup);
52d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  }
53d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
54d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static void printThreadGroupInfo(ThreadGroup tg) {
55d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Object[] threadGroupInfo = getThreadGroupInfo(tg);
56d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    if (threadGroupInfo == null || threadGroupInfo.length != 4) {
57d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      System.out.println(Arrays.toString(threadGroupInfo));
58d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      throw new RuntimeException("threadGroupInfo length wrong");
59d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
60d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
61d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println(tg);
62d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println("  " + threadGroupInfo[0]);  // Parent
63d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println("  " + threadGroupInfo[1]);  // Name
64d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println("  " + threadGroupInfo[2]);  // Priority
65d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println("  " + threadGroupInfo[3]);  // Daemon
66d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  }
67d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
68d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static void checkChildren(ThreadGroup tg) {
69d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Object[] data = getThreadGroupChildren(tg);
70d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Thread[] threads = (Thread[])data[0];
71d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    ThreadGroup[] groups = (ThreadGroup[])data[1];
72d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
734471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    List<Thread> threadList = new ArrayList<>(Arrays.asList(threads));
744471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe
754471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    // Filter out JIT thread. It may or may not be there depending on configuration.
764471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    Iterator<Thread> it = threadList.iterator();
774471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    while (it.hasNext()) {
784471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe      Thread t = it.next();
794471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe      if (t.getName().startsWith("Jit thread pool worker")) {
804471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe        it.remove();
814471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe        break;
824471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe      }
834471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    }
844471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe
854471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    Collections.sort(threadList, THREAD_COMP);
864471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe
87d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Arrays.sort(groups, THREADGROUP_COMP);
88d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println(tg.getName() + ":");
894471e4f7c5874bdaf93762b6047d4a4bebc465dfAndreas Gampe    System.out.println("  " + threadList);
90d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println("  " + Arrays.toString(groups));
91d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
92d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    if (tg.getParent() != null) {
93d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      checkChildren(tg.getParent());
94d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
95d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  }
96d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
97d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)
98d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      throws Exception {
99d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    for (int i = 0; i <  timeoutS; i++) {
100d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      Object[] data = getThreadGroupChildren(tg);
101d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      Thread[] threads = (Thread[])data[0];
102d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      if (threads.length == expectedChildCount) {
103d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe        return;
104d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      }
105d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      Thread.sleep(1000);
106d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
107d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
108d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Object[] data = getThreadGroupChildren(tg);
109d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    Thread[] threads = (Thread[])data[0];
110d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    System.out.println(Arrays.toString(threads));
111d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children.");
112d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  }
113d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
114d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
115d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    public int compare(Thread o1, Thread o2) {
116d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      return o1.getName().compareTo(o2.getName());
117d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
118d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  };
119d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
120d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() {
121d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    public int compare(ThreadGroup o1, ThreadGroup o2) {
122d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe      return o1.getName().compareTo(o2.getName());
123d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe    }
124d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  };
125d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe
126d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static native ThreadGroup[] getTopThreadGroups();
127d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static native Object[] getThreadGroupInfo(ThreadGroup tg);
128d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
129d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe  private static native Object[] getThreadGroupChildren(ThreadGroup tg);
130d18d9e2a94445d4b42e4bc6f0e642e6f76b4706dAndreas Gampe}
131