1 /*
   2  * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  *  @test
  26  *  @bug 5089849
  27  *  @summary Add support for backtracking reference graph.
  28  *  @author jjh
  29  *
  30  *  @run build TestScaffold VMConnection TargetListener TargetAdapter
  31  *  @run compile -g ReferrersTest.java
  32  *  @run driver ReferrersTest
  33  */
  34 
  35 /*
  36  *  To run this test do this:
  37  *     runregress -no ReferrersTest <cmd line options>
  38  *
  39  *  where <cmd line options> are the options to be used to
  40  *  launch the debuggee, with the classname prefixed with @@.
  41  *  For example, this would run java2d demo as the debuggee:
  42  *     runregress -no ReferrersTest -classpath
  43  *                                    $jdkDir/demo/jfc/Java2D/Java2Demo.jar \
  44  *                                    -client @@java2d.Java2Demo
  45  *
  46  * In this mode, the specified debuggee is launched in debug mode,
  47  * and the debugger waits for a keystroke before connecting to the debuggee.
  48  *
  49  * If <cmd line options> is not specified, then the ReferrersTarg class below
  50  * is run as the debuggee.
  51  */
  52 import com.sun.jdi.*;
  53 import com.sun.jdi.event.*;
  54 import com.sun.jdi.request.*;
  55 
  56 import java.util.*;
  57 
  58 class ReferrersFiller {
  59     // This many instances of this are created.
  60     static int FILLER_COUNT = 20000;
  61     static ReferrersFiller[] lotsAndLots = new ReferrersFiller[
  62                                                ReferrersFiller.FILLER_COUNT];
  63     int xx;
  64     ReferrersFiller(int p1) {
  65         xx = p1;
  66     }
  67 }
  68 
  69 class ReferrersTarg {
  70     // This many instances + 1 of this class are created.
  71     static int TARG_COUNT = 10;
  72     static ReferrersTarg theReferrersTarg;
  73     static ReferrersTarg[] allReferrersTargs;
  74 
  75     // Each instance will point to the theReferrersTarg
  76     ReferrersTarg oneReferrersTarg;
  77 
  78     public static void bkpt() {
  79     }
  80 
  81     public static void main(String[] args) {
  82         System.out.println("Howdy!");
  83         for (int ii = 0; ii < ReferrersFiller.lotsAndLots.length; ii++) {
  84             ReferrersFiller.lotsAndLots[ii] = new ReferrersFiller(ii);
  85         }
  86 
  87         theReferrersTarg = new ReferrersTarg();
  88         allReferrersTargs = new ReferrersTarg[ReferrersTarg.TARG_COUNT];
  89         for (int ii = 0; ii < ReferrersTarg.TARG_COUNT; ii++) {
  90             allReferrersTargs[ii] = new ReferrersTarg();
  91             allReferrersTargs[ii].oneReferrersTarg = theReferrersTarg;
  92         }
  93         bkpt();
  94 
  95         System.out.println("Goodbye from ReferrersTarg!");
  96     }
  97 }
  98 
  99 /********** test program **********/
 100 
 101 public class ReferrersTest extends TestScaffold {
 102     static String targetName = "ReferrersTarg";
 103     ReferenceType targetClass;
 104     ThreadReference mainThread;
 105 
 106     ReferrersTest(String args[]) {
 107         super(args);
 108     }
 109 
 110     public static void main(String[] args) throws Exception {
 111         /*
 112          * If args contains @@xxxx, then that is the
 113          * name of the class we are to run.
 114          */
 115         for (int ii = 0; ii < args.length; ii ++) {
 116             if (args[ii].startsWith("@@")) {
 117                 targetName = args[ii] = args[ii].substring(2);
 118                 break;
 119             }
 120         }
 121         new ReferrersTest(args).startTests();
 122     }
 123 
 124     /*
 125      * Used to sort a list of ReferenceTypes by
 126      * instance count.
 127      */
 128     class ToSort implements Comparable<ToSort> {
 129         long count;
 130         ReferenceType rt;
 131 
 132         public ToSort(long count, ReferenceType rt) {
 133             this.count = count;
 134             this.rt = rt;
 135         }
 136 
 137         public int compareTo(ToSort obj) {
 138             if (count < obj.count) return -1;
 139             if (count == obj.count) return 0;
 140             return 1;
 141         }
 142     }
 143 
 144     protected void runTests() throws Exception {
 145         /*
 146          * Get to the top of main()
 147          * to determine targetClass and mainThread
 148          */
 149         int CUT_OFF = 1000;
 150         BreakpointEvent bpe;
 151         bpe = startToMain(targetName);
 152         targetClass = bpe.location().declaringType();
 153         mainThread = bpe.thread();
 154 
 155         if (targetName.equals("ReferrersTarg")) {
 156             resumeTo("ReferrersTarg", "bkpt", "()V");
 157         } else {
 158             // Let debuggee run for awhile to get classes loaded
 159             vm().resume();
 160             try {
 161                 System.err.println("Press <enter> to continue");
 162                 System.in.read();
 163                 System.err.println("running...");
 164 
 165             } catch(Exception e) {
 166             }
 167             vm().suspend();
 168         }
 169 
 170         // Get all classes.
 171         long start = System.currentTimeMillis();
 172         List<ReferenceType> allClasses = vm().allClasses();
 173         long end = System.currentTimeMillis();
 174         System.out.println( allClasses.size() +
 175                             " classes from vm.allClasses() took " +
 176                             (end - start) + " ms");
 177 
 178         long[] counts;
 179 
 180         // Test for NPE
 181         {
 182             boolean pass = false;
 183             try {
 184                 counts = vm().instanceCounts(null);
 185             } catch (NullPointerException ee) {
 186                 pass = true;
 187             }
 188             if (!pass) {
 189                 failure("failure: NullPointerException not thrown on instanceCounts(null)");
 190             }
 191         }
 192 
 193         // Test for 0 length array
 194         {
 195             List<ReferenceType>someClasses = new ArrayList(2);
 196             counts = vm().instanceCounts(someClasses);
 197             if (counts.length != 0) {
 198                 failure("failure: instanceCounts with a zero length array fails: " +
 199                         counts.length);
 200             }
 201         }
 202 
 203         // Test various values of maxInstances
 204         if (targetClass.name().equals("ReferrersTarg")) {
 205             List<ObjectReference> noInstances = targetClass.instances(0);
 206             if (noInstances.size() != ReferrersTarg.TARG_COUNT + 1) {
 207                 failure("failure: instances(0): " + noInstances.size() + ", for " + targetClass);
 208             }
 209             noInstances = targetClass.instances(1);
 210             if (noInstances.size() != 1) {
 211                 failure("failure: instances(1): " + noInstances.size() + ", for " + targetClass);
 212             }
 213             boolean pass = false;
 214             try {
 215                 noInstances = targetClass.instances(-1);
 216             } catch (IllegalArgumentException ee) {
 217                 pass = true;
 218             }
 219             if (!pass) {
 220                 failure("failure: instances(-1) did not get an exception");
 221             }
 222         }
 223 
 224         // Instance counts for all classes
 225         start = System.currentTimeMillis();
 226         counts = vm().instanceCounts(allClasses);
 227         end = System.currentTimeMillis();
 228 
 229         if (counts.length == 0) {
 230             System.out.println("failure: No instances found");
 231             throw new Exception("ReferrersTest: failed");
 232         }
 233 
 234         // Create a list of ReferenceTypes sorted by instance count
 235         int size = 0;
 236         List<ToSort> sorted = new ArrayList(allClasses.size());
 237         for (int ii = 0; ii < allClasses.size(); ii++) {
 238             size += counts[ii];
 239             ToSort tos = new ToSort(counts[ii], allClasses.get(ii));
 240             sorted.add(tos);
 241         }
 242 
 243         System.out.println("instance counts for " + counts.length +
 244                            " classes got " + size + " instances and took " +
 245                             (end - start) + " ms");
 246 
 247 
 248         boolean gotReferrersFiller = false;
 249         boolean gotReferrersTarg = false;
 250 
 251         Collections.sort(sorted);
 252         for (int ii = sorted.size() - 1; ii >= 0 ; ii--) {
 253             ToSort xxx = sorted.get(ii);
 254 
 255             if (xxx.rt.name().equals("ReferrersFiller") &&
 256                 xxx.count == ReferrersFiller.FILLER_COUNT) {
 257                 gotReferrersFiller = true;
 258             }
 259             if (xxx.rt.name().equals("ReferrersTarg") &&
 260                 xxx.count == ReferrersTarg.TARG_COUNT + 1) {
 261                 gotReferrersTarg = true;
 262             }
 263         }
 264         if (!gotReferrersFiller) {
 265             failure("failure: Expected " + ReferrersFiller.FILLER_COUNT +
 266                         " instances of ReferrersFiller");
 267         }
 268         if (!gotReferrersTarg) {
 269             failure("failure: Expected " + (ReferrersTarg.TARG_COUNT + 1) +
 270                     " instances of ReferrersTarg");
 271         }
 272 
 273         List<List<ObjectReference>> allInstances = new ArrayList(10);
 274 
 275         // Instances, one class at a time, in sorted order, printing each line
 276         if (true) {
 277             System.out.println("\nGetting instances for one class " +
 278                                "at a time (limited) in sorted order");
 279             List<ReferenceType> rtList = new ArrayList(1);
 280             rtList.add(null);
 281             long start1 = System.currentTimeMillis();
 282             size = 0;
 283             long count = 0;
 284             for (int ii = sorted.size() - 1; ii >= 0 ; ii--) {
 285                 ToSort xxx = sorted.get(ii);
 286                 if (xxx.count <= CUT_OFF) {
 287                     break;
 288                 }
 289                 rtList.set(0, xxx.rt);
 290                 start = System.currentTimeMillis();
 291                 List<ObjectReference> oneInstances = xxx.rt.instances(19999999);
 292                 end = System.currentTimeMillis();
 293                 size += oneInstances.size();
 294                 count++;
 295                 System.out.println("Expected " + xxx.count + " instances, got " +
 296                                    oneInstances.size() +
 297                                    " instances for " + sorted.get(ii).rt +
 298                                    " in " + (end - start) + " ms");
 299 
 300                 if (xxx.rt.name().equals("ReferrersFiller") &&
 301                     oneInstances.size() != ReferrersFiller.FILLER_COUNT) {
 302                     failure("failure: Expected " + ReferrersFiller.FILLER_COUNT +
 303                             " instances of ReferrersFiller");
 304                 }
 305                 if (xxx.rt.name().equals("ReferrersTarg") &&
 306                     oneInstances.size() != ReferrersTarg.TARG_COUNT + 1) {
 307                     failure("failure: Expected " + (ReferrersTarg.TARG_COUNT + 1) +
 308                             " instances of ReferrersTarg");
 309                 }
 310                 allInstances.add(oneInstances);
 311             }
 312 
 313             end = System.currentTimeMillis();
 314 
 315             System.out.println(size + " instances via making one vm.instances" +
 316                                " call for each of " + count +
 317                                " classes took " + (end - start1) + " ms");
 318             System.out.println("Per class = " +
 319                                (end - start) / allClasses.size() + " ms");
 320         }
 321 
 322 
 323         // referrers
 324 
 325         // Test various values of maxReferrers
 326         if (targetClass.name().equals("ReferrersTarg")) {
 327             Field field1 = targetClass.fieldByName("theReferrersTarg");
 328             ObjectReference anInstance = (ObjectReference)targetClass.getValue(field1);
 329             List<ObjectReference> noReferrers = anInstance.referringObjects(0);
 330             if (noReferrers.size() != ReferrersTarg.TARG_COUNT + 1 ) {
 331                 failure("failure: referringObjects(0) got " + noReferrers.size() +
 332                         ", for " + anInstance);
 333             }
 334             noReferrers = anInstance.referringObjects(1);
 335             if (noReferrers.size() != 1 ) {
 336                 failure("failure: referringObjects(1) got " + noReferrers.size() +
 337                         ", for " + anInstance);
 338             }
 339             boolean pass = false;
 340             try {
 341                 noReferrers = anInstance.referringObjects(-1);
 342             } catch (IllegalArgumentException ee) {
 343                 pass = true;
 344             }
 345             if (!pass) {
 346                 failure("failure: referringObjects(-1) did not get an exception");
 347             }
 348         }
 349 
 350         List<ObjectReference> allReferrers = null;
 351         List<ObjectReference> someInstances = new ArrayList();
 352         if (targetName.equals("ReferrersTarg")) {
 353             Field field1 = targetClass.fieldByName("theReferrersTarg");
 354             ObjectReference val = (ObjectReference)targetClass.getValue(field1);
 355             someInstances.add(val);
 356             allReferrers = val.referringObjects(99999);  //LIMIT
 357             if (allReferrers.size() != ReferrersTarg.TARG_COUNT + 1) {
 358                 failure("failure: expected " + (ReferrersTarg.TARG_COUNT + 1) +
 359                         "referrers, but got " + allReferrers.size() +
 360                         " referrers for " + val);
 361             }
 362         } else {
 363             // referrers
 364             // Create someInstances to find the referrers of.
 365             for (int ii = 0; ii < allClasses.size(); ii++) {
 366                 List<ObjectReference> objRefList = allInstances.get(ii);
 367                 if (objRefList != null) {
 368                     int asize = objRefList.size();
 369                     if (false) {
 370                         System.out.println(asize + ", " + allClasses.get(ii));
 371                     }
 372                     // Remember one instance per class to get referrers
 373                     if (asize > 0) {
 374                         someInstances.add(objRefList.get(0));
 375                     }
 376                 }
 377             }
 378         }
 379 
 380         for (ObjectReference objRef: someInstances) {
 381             //System.out.println( "Getting referrers for " + objRef);
 382             start = System.currentTimeMillis();
 383             if ( true) {
 384                 showReferrers(objRef, 0, 0, 0);
 385             } else {
 386                 allReferrers = objRef.referringObjects(99999);  //LIMIT
 387                 end = System.currentTimeMillis();
 388                 if (true || allReferrers.size() > 1) {
 389                     System.out.println( allReferrers.size() + " referrers for " + objRef + " took " + (end - start) + " ms");
 390                 }
 391             }
 392         }
 393 
 394         /*
 395          * deal with results of test
 396          * if anything has called failure("foo") testFailed will be true
 397          */
 398         if (!testFailed) {
 399             println("ReferrersTest: passed");
 400         } else {
 401             throw new Exception("ReferrersTest: failed");
 402         }
 403     }
 404     void indent(int level) {
 405         for (int ii = 0; ii < level; ii++) {
 406             System.out.print("    ");
 407         }
 408     }
 409 
 410 
 411     Map<ObjectReference, Object> visited = new HashMap(100);
 412     void showReferrers(ObjectReference objRef, int level, int total, int which) {
 413 
 414         if (level == 0) {
 415             visited.clear();
 416         } else {
 417             if (visited.containsKey(objRef)) {
 418                 indent(level);
 419                 System.out.println("(" + which + ")" + ":<pruned> " + objRef);
 420                 return;
 421             }
 422             visited.put(objRef, null);
 423             indent(level);
 424             //System.out.println(which + "/" + total + ": " + objRef + " took " + time + " ms");
 425         }
 426 
 427         List<ObjectReference> allReferrers = null;
 428 
 429         //System.out.println( "Getting referrers for " + objRef);
 430         long start, end;
 431         start = System.currentTimeMillis();
 432         allReferrers = objRef.referringObjects(99999);  //LIMIT
 433         end = System.currentTimeMillis();
 434 
 435         if (which == 0) {
 436             System.out.println(allReferrers.size() + " referrers for " + objRef + " took " + (end - start) + " ms");
 437         } else {
 438             System.out.println("(" + which + ") "  + objRef);
 439             indent(level);
 440             System.out.println("    " + allReferrers.size() + " referrers for " + objRef + " took " + (end - start) + " ms");
 441         }
 442 
 443         // We have to stop going up a referrer chain in some cases
 444         Type rt = objRef.type();
 445         if (rt instanceof ClassType) {
 446             ClassType ct = (ClassType)rt;
 447             String name = ct.name();
 448             if (name.equals("sun.misc.SoftCache$ValueCell")) {
 449                 return;
 450             }
 451             if (name.equals("java.lang.ref.Finalizer")) {
 452                 return;
 453             }
 454             if (name.equals("java.lang.ref.SoftReference")) {
 455                 return;
 456             }
 457             // oh oh, should really check for a subclass of ClassLoader :-)
 458             if (name.indexOf("ClassLoader") >= 0) {
 459                 return;
 460             }
 461             // No doubt there are other reasons to stop ...
 462         }
 463         int itemNumber = 1;
 464         int allSize = allReferrers.size();
 465         for (ObjectReference objx: allReferrers) {
 466             showReferrers(objx, level + 1, allSize, itemNumber++);
 467         }
 468     }
 469 }