1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.lang.ref.WeakReference;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.Semaphore;
24 
25 public class Test1900 {
checkLE(long exp, long o)26   public static void checkLE(long exp, long o) {
27     if (exp > o) {
28       throw new Error("Expected: " + exp + " Got: " + o);
29     }
30   }
checkEq(long exp, long o)31   public static void checkEq(long exp, long o) {
32     if (exp != o) {
33       throw new Error("Expected: " + exp + " Got: " + o);
34     }
35   }
36 
runConcurrent(Runnable... rs)37   public static void runConcurrent(Runnable... rs) throws Exception {
38     final CountDownLatch latch = new CountDownLatch(rs.length);
39     Thread[] thrs = new Thread[rs.length];
40     for (int i = 0; i < rs.length; i++) {
41       final Runnable r = rs[i];
42       thrs[i] = new Thread(() -> {
43         latch.countDown();
44         r.run();
45       });
46       thrs[i].start();
47     }
48     for (Thread thr : thrs) {
49       thr.join();
50     }
51   }
52   static class Holder {
53     public long val;
54   }
55 
run()56   public static void run() throws Exception {
57     initializeTest();
58     // Get the overhead for the native part of this test.
59     final long base_state = getAmountAllocated();
60 
61     // Basic alloc-dealloc
62     checkEq(base_state + 0, getAmountAllocated());
63     long abc = doAllocate(10);
64     checkLE(base_state + 10, getAmountAllocated());
65     long def = doAllocate(10);
66     checkLE(base_state + 20, getAmountAllocated());
67     doDeallocate(abc);
68     checkLE(base_state + 10, getAmountAllocated());
69 
70     doDeallocate(def);
71 
72     checkEq(base_state + 0, getAmountAllocated());
73 
74     // Try doing it concurrently.
75     Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
76     Runnable[] rs = new Runnable[100];
77     Arrays.fill(rs, add10);
78     runConcurrent(rs);
79     checkEq(base_state + 0, getAmountAllocated());
80 
81     // Try doing it concurrently with different threads to allocate and deallocate.
82     final Semaphore sem = new Semaphore(0);
83     final Holder h = new Holder();
84     runConcurrent(
85         () -> {
86           try {
87             h.val = doAllocate(100);
88             checkLE(base_state + 100, getAmountAllocated());
89             sem.release();
90           } catch (Exception e) { throw new Error("exception!", e); }
91         },
92         () -> {
93           try {
94             sem.acquire();
95             long after_acq = getAmountAllocated();
96             doDeallocate(h.val);
97             checkLE(base_state + 100, after_acq);
98           } catch (Exception e) { throw new Error("exception!", e); }
99         }
100     );
101     checkEq(base_state + 0, getAmountAllocated());
102 
103     // Try doing it with multiple jvmtienvs.
104     long env1 = newJvmtiEnv();
105     long env2 = newJvmtiEnv();
106 
107     final long new_base_state = getAmountAllocated();
108     // new jvmtienvs shouldn't save us memory.
109     checkLE(base_state, new_base_state);
110     // Make sure we track both.
111     abc = doAllocate(env1, 10);
112     checkLE(new_base_state + 10, getAmountAllocated());
113     def = doAllocate(env2, 10);
114     checkLE(new_base_state + 20, getAmountAllocated());
115     doDeallocate(env1, abc);
116     checkLE(new_base_state + 10, getAmountAllocated());
117 
118     doDeallocate(env2, def);
119 
120     checkEq(new_base_state + 0, getAmountAllocated());
121 
122     destroyJvmtiEnv(env1);
123     destroyJvmtiEnv(env2);
124 
125     // Back to normal after getting rid of the envs.
126     checkEq(base_state + 0, getAmountAllocated());
127 
128     // Try adding some tags
129     Object a = new Object();
130     Object b = new Object();
131     Main.setTag(a, 100);
132     Main.setTag(b, 200);
133 
134     // tags should be counted and should have some data associated with them.
135     checkLE(base_state + 1, getAmountAllocated());
136   }
137 
doAllocate(long jvmtienv, long size)138   private static native long doAllocate(long jvmtienv, long size);
doAllocate(long size)139   private static long doAllocate(long size) {
140     return doAllocate(getDefaultJvmtiEnv(), size);
141   }
142 
doDeallocate(long jvmtienv, long ptr)143   private static native void doDeallocate(long jvmtienv, long ptr);
doDeallocate(long size)144   private static void doDeallocate(long size) {
145     doDeallocate(getDefaultJvmtiEnv(), size);
146   }
147 
getDefaultJvmtiEnv()148   private static native long getDefaultJvmtiEnv();
newJvmtiEnv()149   private static native long newJvmtiEnv();
destroyJvmtiEnv(long jvmtienv)150   private static native void destroyJvmtiEnv(long jvmtienv);
getAmountAllocated()151   private static native long getAmountAllocated();
initializeTest()152   private static native void initializeTest();
153 }
154