├── .gitignore ├── GcProf.java ├── LICENSE ├── Makefile ├── README.md ├── _test └── Test.java ├── gcprof ├── gcprof.c ├── include └── classfile_constants.h ├── java_crw_demo ├── README.txt ├── java_crw_demo.c ├── java_crw_demo.h └── sample.makefile.txt ├── u.c └── u.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.jnilib 3 | *.o 4 | 5 | -------------------------------------------------------------------------------- /GcProf.java: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Twitter, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import java.lang.reflect.Method; 16 | import java.lang.reflect.Field; 17 | import java.lang.reflect.InvocationTargetException; 18 | import java.util.Arrays; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | import java.util.concurrent.atomic.AtomicLong; 21 | 22 | // todo: do sampling here so that hotspot can 23 | // inline the code. 24 | public class GcProf { 25 | public static native void _new(Object o); 26 | public static native void reset(); 27 | public static native long[] dump(); 28 | 29 | private static long totalAllocation = 0L; 30 | 31 | // This method is designed to be called from java/scala code 32 | // Ex: Class.forName("GcProf").getDeclaredMethod("getTotalAllocation").invoke(null) 33 | public static synchronized long getTotalAllocation() { return totalAllocation; } 34 | 35 | public static void start() { 36 | // Create thread, sleep every now & then, etc. 37 | Thread t = new Thread() { 38 | public void run() { 39 | int[] pcts = {5000, 9000, 9500, 9900, 9990, 9999}; 40 | Integer period = Integer.getInteger("gcprof.period", 1) * 1000; 41 | Integer nwarmup = Integer.getInteger("gcprof.nwarmup", 0); 42 | String nworkpath = System.getProperty("gcprof.nwork"); 43 | String nostrichwork = System.getProperty("gcprof.nostrichwork"); 44 | boolean warm = false; 45 | 46 | long start = System.currentTimeMillis(); 47 | while (true) { 48 | try { 49 | Thread.sleep(period); 50 | } catch (InterruptedException e) { 51 | continue; 52 | } 53 | 54 | long t = System.currentTimeMillis(); 55 | if (t-start < 1000) 56 | continue; 57 | 58 | long nwork; 59 | if (nostrichwork != null) 60 | nwork = getnostrichwork(nostrichwork); 61 | else 62 | nwork = getnwork(nworkpath); 63 | if (nwork < 0) nwork = 1; 64 | 65 | if (!warm) { 66 | if (nwork > nwarmup) { 67 | warm = true; 68 | reset(); 69 | } else 70 | continue; 71 | } 72 | 73 | long[] data = dump(); 74 | long b = data[0]; 75 | long mb = b>>20; 76 | long bpw = 1; 77 | if (nwork > 0) 78 | bpw = b/nwork; 79 | 80 | System.out.printf("%dMB w=%d (%dMB/s %dkB/w)\n", 81 | mb, nwork, 1000*mb/(t-start), bpw>>10); 82 | 83 | if (data.length <= 1) 84 | continue; 85 | 86 | int n = data.length-1; 87 | Arrays.sort(data, 1, 1+n); 88 | 89 | synchronized(this) { 90 | totalAllocation = mb; 91 | } 92 | 93 | for(int i=0 ; i>20; 96 | long allocRatePerReq = data[((n*p)/10000)+1]/bpw; 97 | System.out.printf("%02d.%02d%%\t%d\t%d\n", 98 | p/100, 99 | p-((p/100)*100), 100 | allocRate, 101 | allocRatePerReq); 102 | } 103 | } 104 | } 105 | }; 106 | t.setDaemon(true); 107 | t.start(); 108 | } 109 | 110 | static private long getnwork(String path) { 111 | if (path == null) 112 | return -1L; 113 | 114 | String[] ps = splitPath(path); 115 | return getlong(getClass(ps[0]), ps[1]); 116 | } 117 | 118 | static private long getnostrichwork(String name) { 119 | Class cls = getClass("com.twitter.ostrich.stats.Stats$"); 120 | 121 | try { 122 | Object o = cls.getField("MODULE$").get(null); 123 | Method m = o.getClass().getMethod("get", String.class); 124 | o = m.invoke(o, ""); 125 | m = o.getClass().getMethod("getCounter", String.class); 126 | o = m.invoke(o, name); 127 | m = o.getClass().getMethod("apply"); 128 | o = m.invoke(o); 129 | return (Long)o; 130 | } catch (Exception e) { 131 | e.printStackTrace(); 132 | panic("failed to load ostrich "+e); 133 | return -1; 134 | } 135 | } 136 | 137 | static private void panic(String msg) { 138 | System.err.println(msg); 139 | System.exit(1); 140 | } 141 | 142 | static private long getlong(Class cls, String member) { 143 | Object o = getMember(cls, member); 144 | 145 | if (o instanceof AtomicInteger) 146 | return ((AtomicInteger)o).get(); 147 | if(o instanceof AtomicLong) 148 | return ((AtomicLong)o).get(); 149 | if (o instanceof Number) 150 | return ((Number)o).longValue(); 151 | 152 | panic("could not find object in "+member); 153 | return -1L; 154 | } 155 | 156 | static private String[] splitPath(String path) { 157 | String[] result = {}; 158 | if (path == null) 159 | panic("bad path: null"); 160 | else { 161 | result = path.split(":"); 162 | if (result.length != 2) 163 | panic("bad path "+path); 164 | } 165 | return result; 166 | } 167 | 168 | static private Object getMember(Class cls, String path) { 169 | String ps[] = path.split("\\.", 2); 170 | Object o = null; 171 | 172 | for (String p : ps) { 173 | try { 174 | Method m = cls.getMethod(p); 175 | m.setAccessible(true); 176 | o = m.invoke(o); 177 | cls = o.getClass(); 178 | continue; 179 | } catch (NoSuchMethodException e) { 180 | } catch (IllegalAccessException e) { 181 | panic("IllegalAccessException ("+p+") in "+path); 182 | } catch (InvocationTargetException e) { 183 | panic("InvocationTargetException ("+p+") in "+path); 184 | } 185 | 186 | try { 187 | Field f = cls.getField(p); 188 | f.setAccessible(true); 189 | o = f.get(o); 190 | cls = o.getClass(); 191 | continue; 192 | } catch (NoSuchFieldException e) { 193 | panic("cannot find \""+path+"\" in "+cls); 194 | } catch (IllegalAccessException e) { 195 | panic("IllegalAccessException ("+p+") in "+path); 196 | } 197 | 198 | panic("cannot find \""+p+"\" in "+cls); 199 | } 200 | 201 | return o; 202 | } 203 | 204 | static private Class getClass(String fullClassName) { 205 | ClassLoader loader = getloader(); 206 | Class cls = null; 207 | 208 | try { 209 | cls = Class.forName(fullClassName, true, loader); 210 | } catch (ClassNotFoundException e) { 211 | panic("cannot find class \"" + fullClassName + "\""); 212 | } 213 | 214 | return cls; 215 | } 216 | 217 | static private ClassLoader getloader() { 218 | ThreadGroup g = Thread.currentThread().getThreadGroup(); 219 | for (;;) { 220 | ThreadGroup g0 = g.getParent(); 221 | if (g0 == null) 222 | break; 223 | g = g0; 224 | } 225 | 226 | Thread[] active = new Thread[g.activeCount()]; 227 | g.enumerate(active); 228 | 229 | ClassLoader loader = ClassLoader.getSystemClassLoader(); 230 | for (Thread t : active) 231 | if (t != Thread.currentThread()) 232 | loader = t.getContextClassLoader(); 233 | 234 | return loader; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | OS=$(shell uname -s | tr '[A-Z]' '[a-z]') 3 | 4 | ifeq ("$(OS)", "darwin") 5 | JAVA_HOME?=$(shell /usr/libexec/java_home) 6 | JAVA_HEADERS?=/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers 7 | #JAVA_HEADERS?=/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Headers/ 8 | PLATFORM_LDFLAGS=-mimpure-text 9 | endif 10 | 11 | ifeq ("$(OS)", "linux") 12 | JAVA_HOME?=/usr/java/default/ 13 | JAVA_HEADERS=$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux 14 | endif 15 | 16 | CFLAGS=-Ijava_crw_demo -fno-strict-aliasing \ 17 | -fPIC -fno-omit-frame-pointer -W -Wall -Wno-unused -Wno-parentheses \ 18 | -I$(JAVA_HEADERS) -Iinclude 19 | LDFLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer \ 20 | -static-libgcc -shared $(PLATFORM_LDFLAGS) 21 | 22 | all: libgcprof.jnilib GcProf.class 23 | 24 | libgcprof.jnilib: gcprof.o u.o java_crw_demo/java_crw_demo.o 25 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lc 26 | 27 | %.class: %.java 28 | javac -Xlint:unchecked $< 29 | 30 | clean: 31 | rm -f *.o 32 | rm -f libgcprof.jnilib 33 | rm -f java_crw_demo/*.o 34 | 35 | .PHONY: all clean 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gcprof 2 | 3 | gcprof is a simple utility for profile allocation and garbage collection activity in the JVM 4 | 5 | ## Usage 6 | 7 | gcprof [-p period] [-n class:fieldpath] [-no ostrichcounter] [-w nwarmup] java ... 8 | 9 | Profile allocation and garbage collection activity in the JVM. The 10 | gcprof command runs a java command under profiling. Allocation and 11 | collection statistics are printed periodically. If -n or -no are 12 | provided, statistics are also reported in terms of the given 13 | application metric. Total allocation, allocation rate, and a 14 | survival histogram is given. The intended use for this tool is 15 | twofold: (1) monitor and test garbage allocation and GC behavior, 16 | and (2) inform GC tuning. 17 | 18 | For example: 19 | 20 | 903MB w=5432 (12MB/s 170kB/w) 21 | 50.00% 8 48 22 | 90.00% 14 88 23 | 95.00% 15 92 24 | 99.00% 16 97 25 | 99.90% 30 182 26 | 99.99% 47 288 27 | 28 | Reports 903MB of total allocation, with the amount of work being 29 | 5432 (in this example, requests processed by a server). The 30 | allocation rate is 12 MB/s, or 170kB per request. 50.00% of 31 | allocated objects survive only for 8MB of allocation (or processing 32 | 48 requests). By 47 MB of allocation or 288 requests processed 33 | 99.99% of the objects have died. 34 | 35 | An application metric can be integrated through the use of "-n". 36 | Given a class:fieldpath. -n names a longs, AtomicInteger or 37 | AtomicLong. The components in the fieldpath can either be fields or 38 | nullary methods. For example 39 | 40 | package foo.bar; 41 | class Foo{ 42 | public static Baz baz = … 43 | } 44 | class Baz{ 45 | Biz biz() { 46 | … 47 | } 48 | } 49 | class Biz{ 50 | AtomicInteger count = … 51 | } 52 | 53 | ``count'' is named by the string "foo.bar.Foo.baz.biz.count". If 54 | you are using Ostrich[1], an application metric can be provided by 55 | naming an ostrich counter with the -no flag. 56 | 57 | The reporting period (in seconds) is specified with -p, defaulting 58 | to 1. 59 | 60 | If -w is given, data is gathered only after that amount of work has 61 | been done according to the work metric. 62 | 63 | The statistics are subject to the current garbage collector (and its 64 | settings). 65 | 66 | ## Examples 67 | 68 | A new load balancer in finagle[2] drastically improved allocation 69 | behavior when load balancing over large numbers of hosts. To 70 | observe this, we ran a stress test under gcprof with the old & new 71 | balancers: 72 | 73 | % gcprof -n com.twitter.finagle.stress.LoadBalancerTest$:MODULE$.totalRequests\ 74 | $s/bin/scala -i finagle/finagle-stress com.twitter.finagle.stress.LoadBalancerTest 75 | 76 | Old: 77 | 78 | 6335MB w=5348 (369MB/s 1213kB/w) 79 | 50.00% 10 8 80 | 90.00% 18 15 81 | 95.00% 25 21 82 | 99.00% 453 382 83 | 99.90% 813 687 84 | 99.99% 4966 4192 85 | 86 | New: 87 | 88 | 2797MB w=101223 (231MB/s 28kB/w) 89 | 50.00% 8 297 90 | 90.00% 14 542 91 | 95.00% 15 572 92 | 99.00% 61 2237 93 | 99.90% 2620 94821 94 | 99.99% 2652 95974 95 | 96 | The new load balancer results in two orders of magnitude smaller 97 | allocation rate per request (it is also faster, hence the observe 98 | the total memory throughput is only slightly smaller). The survival 99 | distribution also changed: the tail is longer in requests processed 100 | but shorter in allocation. Presumably this is due to the improved 101 | throughput of the balancer. 102 | 103 | ## Bugs 104 | 105 | In the remote possibility that there exist bugs in this code, please report them to: 106 | 107 | 108 | * Note: currently works only on x86_64 109 | 110 | ## TODO 111 | 112 | * maintain windowed averages 113 | * nallocations (though somewhat less relevant--fun to know?) 114 | 115 | [1] http://github.com/twitter/ostrich 116 | [2] http://github.com/twitter/finagle 117 | 118 | ## Authors 119 | * Marius Eriksen 120 | 121 | Thanks for assistance and contributions: 122 | 123 | * Steve Gury 124 | 125 | ## License 126 | Copyright 2012 Twitter, Inc. 127 | 128 | Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 129 | -------------------------------------------------------------------------------- /_test/Test.java: -------------------------------------------------------------------------------- 1 | import java.lang.reflect.Method; 2 | import java.util.Arrays; 3 | import java.util.Random; 4 | 5 | class Test{ 6 | public static long count = 0; 7 | public static void main(String[] args){ 8 | try{ 9 | Class klass; 10 | Method reset; 11 | Method dump; 12 | Random r = new Random(); 13 | 14 | klass = Class.forName("GcProf"); 15 | reset = klass.getDeclaredMethod("reset"); 16 | dump = klass.getDeclaredMethod("dump"); 17 | if(dump == null) 18 | throw new Exception("sigh"); 19 | reset.invoke(null); 20 | dump.invoke(null); 21 | 22 | long start = System.currentTimeMillis(); 23 | 24 | int i = 0; 25 | while(true) { 26 | if(false && i++ % 1000 == 0){ 27 | long elapsed = System.currentTimeMillis() - start; 28 | long[] data = (long[])dump.invoke(null); 29 | if(data.length>1) 30 | Arrays.sort(data, 1, data.length-1); 31 | if(elapsed > 0) 32 | System.out.printf("total: %d (%d bpms / %dmbps)\n", 33 | data[0], data[0]/elapsed, (1000*data[0]>>20)/elapsed); 34 | // for(int j=1; j 17 | #include "java_crw_demo.h" 18 | 19 | enum 20 | { 21 | Maxsample = 10000, 22 | }; 23 | 24 | typedef struct Galloc Galloc; 25 | struct Galloc 26 | { 27 | uint64 n; 28 | uint64 tot; 29 | long rnd; 30 | }; 31 | 32 | typedef struct Gsample Gsample; 33 | struct Gsample 34 | { 35 | long rnd; 36 | uint64 age; 37 | }; 38 | 39 | static int gcprofinit(); 40 | static void lock(); 41 | static void unlock(); 42 | static void fixup(int i); 43 | static void fixdown(int i, int j); 44 | static int jlongcmp(const void *a, const void *b); 45 | static inline uint64 atomicinc64(volatile uint64 *u, uint64 incr); 46 | static inline void mfence(); 47 | 48 | JNIEXPORT void JNICALL jniNew(JNIEnv*, jclass, jobject); 49 | JNIEXPORT void JNICALL jniReset(JNIEnv*, jclass); 50 | JNIEXPORT jlongArray JNICALL jniDump(JNIEnv*, jclass); 51 | 52 | static void JNICALL jvmtiVMStart(jvmtiEnv* jvmti, JNIEnv* env); 53 | static void JNICALL jvmtiVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread); 54 | static void JNICALL jvmtiVMDeath(jvmtiEnv* jvmti, JNIEnv* env); 55 | static void JNICALL jvmtiObjectFree(jvmtiEnv* jvmti, jlong tag); 56 | static void JNICALL jvmtiClassFileLoadHook( 57 | jvmtiEnv*, JNIEnv*, jclass, jobject, const char*, 58 | jobject, jint, const unsigned char*, 59 | jint*, unsigned char**); 60 | 61 | static jvmtiEnv *jvmti; 62 | static const char *Gcprofclass = "GcProf"; 63 | static const char *Gcprofclasssig = "LGcProf;"; 64 | static int started = 0; 65 | static jrawMonitorID monitor; 66 | static struct 67 | { 68 | uint64 rst; 69 | uint64 tot; 70 | uint64 ntagged; 71 | uint64 nrejected; 72 | } stats; 73 | static Gsample sample[Maxsample+1]; 74 | static int nsample = 0; 75 | 76 | JNIEXPORT jint JNICALL 77 | Agent_OnLoad(JavaVM *jvm, char *options, void *_ignore) 78 | { 79 | jint rv; 80 | 81 | rv = (*jvm)->GetEnv(jvm, (void**)&jvmti, JVMTI_VERSION_1_0); 82 | if(rv != JNI_OK || jvmti == nil) 83 | panic("failed to access JVMTIv1"); 84 | 85 | if(gcprofinit() < 0) 86 | panic("failed to initialize gcprof"); 87 | 88 | return JNI_OK; 89 | } 90 | 91 | static int 92 | gcprofinit() 93 | { 94 | jvmtiCapabilities c; 95 | jvmtiEvent events[] = { 96 | JVMTI_EVENT_VM_START, 97 | JVMTI_EVENT_VM_INIT, 98 | JVMTI_EVENT_VM_DEATH, 99 | JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, 100 | JVMTI_EVENT_OBJECT_FREE 101 | }; 102 | uint i; 103 | int rv; 104 | 105 | rv = (*jvmti)->CreateRawMonitor(jvmti, "gcprof", &monitor); 106 | if(rv != JVMTI_ERROR_NONE) 107 | panic("CreateRawMonitor"); 108 | 109 | stats.tot = stats.rst = stats.ntagged = stats.nrejected = 0; 110 | 111 | memset(&c, 0, sizeof(c)); 112 | c.can_generate_all_class_hook_events = 1; 113 | c.can_tag_objects = 1; 114 | c.can_generate_object_free_events = 1; 115 | 116 | if((*jvmti)->AddCapabilities(jvmti, &c) != JVMTI_ERROR_NONE) 117 | return -1; 118 | 119 | jvmtiEventCallbacks cb; 120 | memset(&cb, 0, sizeof(cb)); 121 | cb.VMStart = &jvmtiVMStart; 122 | cb.VMInit = &jvmtiVMInit; 123 | cb.VMDeath = &jvmtiVMDeath; 124 | cb.ObjectFree = &jvmtiObjectFree; 125 | cb.ClassFileLoadHook = &jvmtiClassFileLoadHook; 126 | rv = (*jvmti)->SetEventCallbacks(jvmti, &cb, (jint)sizeof cb); 127 | if(rv != JVMTI_ERROR_NONE) 128 | return -1; 129 | 130 | for(i=0; iSetEventNotificationMode( 132 | jvmti, JVMTI_ENABLE, events[i], nil); 133 | if(rv != JVMTI_ERROR_NONE) 134 | return -1; 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | static void 141 | lock() 142 | { 143 | if((*jvmti)->RawMonitorEnter(jvmti, monitor) != JVMTI_ERROR_NONE) 144 | panic("RawMonitorEnter"); 145 | } 146 | 147 | static void 148 | unlock() 149 | { 150 | if((*jvmti)->RawMonitorExit(jvmti, monitor) != JVMTI_ERROR_NONE) 151 | panic("RawMonitorExit"); 152 | } 153 | 154 | static void 155 | fixup(int i) 156 | { 157 | Gsample x; 158 | 159 | while(i != 1 && sample[i].rnd > sample[i/2].rnd){ 160 | x = sample[i]; 161 | sample[i] = sample[i/2]; 162 | sample[i/2] = x; 163 | i /= 2; 164 | } 165 | } 166 | 167 | static void 168 | fixdown(int i, int j) 169 | { 170 | int m; 171 | Gsample x; 172 | 173 | loop: 174 | if(j < 2*i) 175 | return; 176 | 177 | if(j == 2*i || sample[2*i].rnd > sample[2*i+1].rnd) 178 | m = 2*i; 179 | else 180 | m = 2*i+1; 181 | 182 | if(sample[m].rnd > sample[i].rnd){ 183 | x = sample[i]; 184 | sample[i] = sample[m]; 185 | sample[m] = x; 186 | i = m; 187 | goto loop; 188 | } 189 | } 190 | 191 | JNIEXPORT void JNICALL 192 | jniNew(JNIEnv* env, jclass class, jobject o) 193 | { 194 | jlong n; 195 | long rnd; 196 | Galloc *g; 197 | 198 | if((*jvmti)->GetObjectSize(jvmti, o, &n) != JVMTI_ERROR_NONE) 199 | panic("GetObjectSize"); 200 | 201 | 202 | // keep track of nallocations as well. 203 | 204 | atomicinc64(&stats.tot, n); 205 | rnd = rand(); 206 | 207 | // Because the max(heap) is monotonically decreasing, 208 | // this test is safe. 209 | if(nsample > 0 && rnd > sample[1].rnd) 210 | return; 211 | 212 | // Slow path: we're sampled! 213 | g = emalloc(sizeof *g); 214 | g->rnd = rnd; 215 | g->n = n; 216 | g->tot = stats.tot; 217 | (*jvmti)->SetTag(jvmti, o, (long)g); 218 | atomicinc64(&stats.ntagged, 1); 219 | } 220 | 221 | JNIEXPORT void JNICALL 222 | jniReset(JNIEnv *env, jclass class) 223 | { 224 | lock(); 225 | nsample = 0; 226 | stats.rst = stats.tot; 227 | unlock(); 228 | } 229 | 230 | JNIEXPORT jlongArray JNICALL 231 | jniDump(JNIEnv *env, jclass class) 232 | { 233 | jlongArray a; 234 | jlong tot, *snap; 235 | int i, n; 236 | 237 | lock(); 238 | n = nsample; 239 | snap = emalloc(n*sizeof(jlong)); 240 | 241 | for(i=0; iNewLongArray(env, n+1); 249 | (*env)->SetLongArrayRegion(env, a, 0, 1, &tot); 250 | if(n > 0) 251 | (*env)->SetLongArrayRegion(env, a, 1, n, snap); 252 | 253 | free(snap); 254 | 255 | return a; 256 | } 257 | 258 | static void JNICALL 259 | jvmtiVMStart(jvmtiEnv* jvmti, JNIEnv* env) 260 | { 261 | jmethodID mid; 262 | 263 | JNINativeMethod registry[] = { 264 | {"_new", "(Ljava/lang/Object;)V", &jniNew}, 265 | {"reset", "()V", &jniReset}, 266 | {"dump", "()[J", &jniDump}, 267 | }; 268 | jclass class; 269 | 270 | if((class=(*env)->FindClass(env, Gcprofclass)) == nil) 271 | panic("Failed to find helper class %s", Gcprofclass); 272 | 273 | // TODO: does this need to be protected? 274 | if((*env)->RegisterNatives(env, class, registry, nelem(registry)) != JNI_OK) 275 | panic("Failed to register natives"); 276 | 277 | started = 1; 278 | mfence(); 279 | } 280 | 281 | static void 282 | JNICALL jvmtiVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) 283 | { 284 | jmethodID mid; 285 | jclass class; 286 | 287 | if((class=(*env)->FindClass(env, Gcprofclass)) == nil) 288 | panic("Failed to find helper class %s", Gcprofclass); 289 | 290 | if((mid=(*env)->GetStaticMethodID(env, class, "start", "()V")) == nil) 291 | panic("GetStaticMethodID"); 292 | 293 | (*env)->CallStaticVoidMethod(env, class, mid); 294 | } 295 | 296 | static void JNICALL 297 | jvmtiVMDeath(jvmtiEnv* jvmti, JNIEnv* env) 298 | {} 299 | 300 | static void JNICALL 301 | jvmtiObjectFree(jvmtiEnv* jvmti, jlong tag) 302 | { 303 | Galloc *g; 304 | long rnd; 305 | uint64 age; 306 | 307 | g = (Galloc*)tag; 308 | age = stats.tot - g->tot; 309 | 310 | lock(); 311 | if(g->tot < stats.rst){ 312 | }else if(nsamplernd; 314 | sample[nsample].age = age; 315 | fixup(nsample); 316 | }else if(g->rnd < sample[1].rnd){ 317 | sample[1].rnd = g->rnd; 318 | sample[1].age = age; 319 | fixdown(1, nsample); 320 | }else 321 | atomicinc64(&stats.nrejected, 1); 322 | 323 | unlock(); 324 | //mfence(); 325 | free(g); 326 | } 327 | 328 | static void JNICALL 329 | jvmtiClassFileLoadHook( 330 | jvmtiEnv* jvmti, JNIEnv* env, 331 | jclass class, jobject loader, 332 | const char* _name, jobject protdomain, 333 | jint nclassdata, const unsigned char* classdata, 334 | jint* nnewclassdata, unsigned char** newclassdata) 335 | { 336 | uchar *image; 337 | long nimage; 338 | char *name; 339 | char *p; 340 | 341 | if (_name == nil) 342 | name = java_crw_demo_classname( 343 | classdata, nclassdata, nil); 344 | else 345 | name = estrdup((char*)_name); 346 | 347 | if(strcmp(name, Gcprofclass) == 0) 348 | goto done; 349 | 350 | java_crw_demo( 351 | 0, 352 | name, 353 | classdata, nclassdata, 354 | !started, // system class 355 | (char*)Gcprofclass, 356 | (char*)Gcprofclasssig, 357 | nil, nil, 358 | nil, nil, 359 | "_new", "(Ljava/lang/Object;)V", 360 | "_new", "(Ljava/lang/Object;)V", 361 | &image, &nimage, 362 | (void*)nil, (void*)nil); 363 | 364 | if(nimage > 0L){ 365 | if((*jvmti)->Allocate(jvmti, nimage, (uchar**)&p) != JVMTI_ERROR_NONE) 366 | panic("Allocate"); 367 | memcpy(p, image, nimage); 368 | *nnewclassdata = nimage; 369 | *newclassdata = (uchar*)p; 370 | } 371 | 372 | if(image != nil) 373 | free(image); 374 | 375 | done: 376 | free(name); 377 | } 378 | 379 | static int 380 | jlongcmp(const void *a, const void *b) 381 | { 382 | uint64 ua, ub; 383 | ua = *(jlong*)a; 384 | ub = *(jlong*)b; 385 | 386 | if(ua < ub) 387 | return -1; 388 | else if(ub < ua) 389 | return 1; 390 | else 391 | return 0; 392 | } 393 | 394 | static inline uint64 395 | atomicinc64(volatile uint64 *u, uint64 incr) 396 | { 397 | uint64 x; 398 | x = incr; 399 | 400 | __asm__ __volatile__( 401 | "lock;" 402 | "xaddq %0, %1" 403 | : "+r" (x), "+m" (*u) 404 | : 405 | : "memory" 406 | ); 407 | 408 | return x+incr; 409 | } 410 | 411 | static inline void 412 | mfence() 413 | { 414 | __asm__ __volatile__( 415 | "mfence" 416 | : 417 | : 418 | : "memory" 419 | ); 420 | } -------------------------------------------------------------------------------- /include/classfile_constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @(#)classfile_constants.h 1.4 05/11/17 3 | * 4 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. 5 | * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 | * 7 | */ 8 | 9 | #ifndef CLASSFILE_CONSTANTS_H 10 | #define CLASSFILE_CONSTANTS_H 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /* Flags */ 17 | 18 | enum { 19 | JVM_ACC_PUBLIC = 0x0001, 20 | JVM_ACC_PRIVATE = 0x0002, 21 | JVM_ACC_PROTECTED = 0x0004, 22 | JVM_ACC_STATIC = 0x0008, 23 | JVM_ACC_FINAL = 0x0010, 24 | JVM_ACC_SYNCHRONIZED = 0x0020, 25 | JVM_ACC_SUPER = 0x0020, 26 | JVM_ACC_VOLATILE = 0x0040, 27 | JVM_ACC_BRIDGE = 0x0040, 28 | JVM_ACC_TRANSIENT = 0x0080, 29 | JVM_ACC_VARARGS = 0x0080, 30 | JVM_ACC_NATIVE = 0x0100, 31 | JVM_ACC_INTERFACE = 0x0200, 32 | JVM_ACC_ABSTRACT = 0x0400, 33 | JVM_ACC_STRICT = 0x0800, 34 | JVM_ACC_SYNTHETIC = 0x1000, 35 | JVM_ACC_ANNOTATION = 0x2000, 36 | JVM_ACC_ENUM = 0x4000 37 | }; 38 | 39 | /* Used in newarray instruction. */ 40 | 41 | enum { 42 | JVM_T_BOOLEAN = 4, 43 | JVM_T_CHAR = 5, 44 | JVM_T_FLOAT = 6, 45 | JVM_T_DOUBLE = 7, 46 | JVM_T_BYTE = 8, 47 | JVM_T_SHORT = 9, 48 | JVM_T_INT = 10, 49 | JVM_T_LONG = 11 50 | }; 51 | 52 | /* Constant Pool Entries */ 53 | 54 | enum { 55 | JVM_CONSTANT_Utf8 = 1, 56 | JVM_CONSTANT_Unicode = 2, /* unused */ 57 | JVM_CONSTANT_Integer = 3, 58 | JVM_CONSTANT_Float = 4, 59 | JVM_CONSTANT_Long = 5, 60 | JVM_CONSTANT_Double = 6, 61 | JVM_CONSTANT_Class = 7, 62 | JVM_CONSTANT_String = 8, 63 | JVM_CONSTANT_Fieldref = 9, 64 | JVM_CONSTANT_Methodref = 10, 65 | JVM_CONSTANT_InterfaceMethodref = 11, 66 | JVM_CONSTANT_NameAndType = 12 67 | }; 68 | 69 | /* StackMapTable type item numbers */ 70 | 71 | enum { 72 | JVM_ITEM_Top = 0, 73 | JVM_ITEM_Integer = 1, 74 | JVM_ITEM_Float = 2, 75 | JVM_ITEM_Double = 3, 76 | JVM_ITEM_Long = 4, 77 | JVM_ITEM_Null = 5, 78 | JVM_ITEM_UninitializedThis = 6, 79 | JVM_ITEM_Object = 7, 80 | JVM_ITEM_Uninitialized = 8 81 | }; 82 | 83 | /* Type signatures */ 84 | 85 | enum { 86 | JVM_SIGNATURE_ARRAY = '[', 87 | JVM_SIGNATURE_BYTE = 'B', 88 | JVM_SIGNATURE_CHAR = 'C', 89 | JVM_SIGNATURE_CLASS = 'L', 90 | JVM_SIGNATURE_ENDCLASS = ';', 91 | JVM_SIGNATURE_ENUM = 'E', 92 | JVM_SIGNATURE_FLOAT = 'F', 93 | JVM_SIGNATURE_DOUBLE = 'D', 94 | JVM_SIGNATURE_FUNC = '(', 95 | JVM_SIGNATURE_ENDFUNC = ')', 96 | JVM_SIGNATURE_INT = 'I', 97 | JVM_SIGNATURE_LONG = 'J', 98 | JVM_SIGNATURE_SHORT = 'S', 99 | JVM_SIGNATURE_VOID = 'V', 100 | JVM_SIGNATURE_BOOLEAN = 'Z' 101 | }; 102 | 103 | /* Opcodes */ 104 | 105 | enum { 106 | JVM_OPC_nop = 0, 107 | JVM_OPC_aconst_null = 1, 108 | JVM_OPC_iconst_m1 = 2, 109 | JVM_OPC_iconst_0 = 3, 110 | JVM_OPC_iconst_1 = 4, 111 | JVM_OPC_iconst_2 = 5, 112 | JVM_OPC_iconst_3 = 6, 113 | JVM_OPC_iconst_4 = 7, 114 | JVM_OPC_iconst_5 = 8, 115 | JVM_OPC_lconst_0 = 9, 116 | JVM_OPC_lconst_1 = 10, 117 | JVM_OPC_fconst_0 = 11, 118 | JVM_OPC_fconst_1 = 12, 119 | JVM_OPC_fconst_2 = 13, 120 | JVM_OPC_dconst_0 = 14, 121 | JVM_OPC_dconst_1 = 15, 122 | JVM_OPC_bipush = 16, 123 | JVM_OPC_sipush = 17, 124 | JVM_OPC_ldc = 18, 125 | JVM_OPC_ldc_w = 19, 126 | JVM_OPC_ldc2_w = 20, 127 | JVM_OPC_iload = 21, 128 | JVM_OPC_lload = 22, 129 | JVM_OPC_fload = 23, 130 | JVM_OPC_dload = 24, 131 | JVM_OPC_aload = 25, 132 | JVM_OPC_iload_0 = 26, 133 | JVM_OPC_iload_1 = 27, 134 | JVM_OPC_iload_2 = 28, 135 | JVM_OPC_iload_3 = 29, 136 | JVM_OPC_lload_0 = 30, 137 | JVM_OPC_lload_1 = 31, 138 | JVM_OPC_lload_2 = 32, 139 | JVM_OPC_lload_3 = 33, 140 | JVM_OPC_fload_0 = 34, 141 | JVM_OPC_fload_1 = 35, 142 | JVM_OPC_fload_2 = 36, 143 | JVM_OPC_fload_3 = 37, 144 | JVM_OPC_dload_0 = 38, 145 | JVM_OPC_dload_1 = 39, 146 | JVM_OPC_dload_2 = 40, 147 | JVM_OPC_dload_3 = 41, 148 | JVM_OPC_aload_0 = 42, 149 | JVM_OPC_aload_1 = 43, 150 | JVM_OPC_aload_2 = 44, 151 | JVM_OPC_aload_3 = 45, 152 | JVM_OPC_iaload = 46, 153 | JVM_OPC_laload = 47, 154 | JVM_OPC_faload = 48, 155 | JVM_OPC_daload = 49, 156 | JVM_OPC_aaload = 50, 157 | JVM_OPC_baload = 51, 158 | JVM_OPC_caload = 52, 159 | JVM_OPC_saload = 53, 160 | JVM_OPC_istore = 54, 161 | JVM_OPC_lstore = 55, 162 | JVM_OPC_fstore = 56, 163 | JVM_OPC_dstore = 57, 164 | JVM_OPC_astore = 58, 165 | JVM_OPC_istore_0 = 59, 166 | JVM_OPC_istore_1 = 60, 167 | JVM_OPC_istore_2 = 61, 168 | JVM_OPC_istore_3 = 62, 169 | JVM_OPC_lstore_0 = 63, 170 | JVM_OPC_lstore_1 = 64, 171 | JVM_OPC_lstore_2 = 65, 172 | JVM_OPC_lstore_3 = 66, 173 | JVM_OPC_fstore_0 = 67, 174 | JVM_OPC_fstore_1 = 68, 175 | JVM_OPC_fstore_2 = 69, 176 | JVM_OPC_fstore_3 = 70, 177 | JVM_OPC_dstore_0 = 71, 178 | JVM_OPC_dstore_1 = 72, 179 | JVM_OPC_dstore_2 = 73, 180 | JVM_OPC_dstore_3 = 74, 181 | JVM_OPC_astore_0 = 75, 182 | JVM_OPC_astore_1 = 76, 183 | JVM_OPC_astore_2 = 77, 184 | JVM_OPC_astore_3 = 78, 185 | JVM_OPC_iastore = 79, 186 | JVM_OPC_lastore = 80, 187 | JVM_OPC_fastore = 81, 188 | JVM_OPC_dastore = 82, 189 | JVM_OPC_aastore = 83, 190 | JVM_OPC_bastore = 84, 191 | JVM_OPC_castore = 85, 192 | JVM_OPC_sastore = 86, 193 | JVM_OPC_pop = 87, 194 | JVM_OPC_pop2 = 88, 195 | JVM_OPC_dup = 89, 196 | JVM_OPC_dup_x1 = 90, 197 | JVM_OPC_dup_x2 = 91, 198 | JVM_OPC_dup2 = 92, 199 | JVM_OPC_dup2_x1 = 93, 200 | JVM_OPC_dup2_x2 = 94, 201 | JVM_OPC_swap = 95, 202 | JVM_OPC_iadd = 96, 203 | JVM_OPC_ladd = 97, 204 | JVM_OPC_fadd = 98, 205 | JVM_OPC_dadd = 99, 206 | JVM_OPC_isub = 100, 207 | JVM_OPC_lsub = 101, 208 | JVM_OPC_fsub = 102, 209 | JVM_OPC_dsub = 103, 210 | JVM_OPC_imul = 104, 211 | JVM_OPC_lmul = 105, 212 | JVM_OPC_fmul = 106, 213 | JVM_OPC_dmul = 107, 214 | JVM_OPC_idiv = 108, 215 | JVM_OPC_ldiv = 109, 216 | JVM_OPC_fdiv = 110, 217 | JVM_OPC_ddiv = 111, 218 | JVM_OPC_irem = 112, 219 | JVM_OPC_lrem = 113, 220 | JVM_OPC_frem = 114, 221 | JVM_OPC_drem = 115, 222 | JVM_OPC_ineg = 116, 223 | JVM_OPC_lneg = 117, 224 | JVM_OPC_fneg = 118, 225 | JVM_OPC_dneg = 119, 226 | JVM_OPC_ishl = 120, 227 | JVM_OPC_lshl = 121, 228 | JVM_OPC_ishr = 122, 229 | JVM_OPC_lshr = 123, 230 | JVM_OPC_iushr = 124, 231 | JVM_OPC_lushr = 125, 232 | JVM_OPC_iand = 126, 233 | JVM_OPC_land = 127, 234 | JVM_OPC_ior = 128, 235 | JVM_OPC_lor = 129, 236 | JVM_OPC_ixor = 130, 237 | JVM_OPC_lxor = 131, 238 | JVM_OPC_iinc = 132, 239 | JVM_OPC_i2l = 133, 240 | JVM_OPC_i2f = 134, 241 | JVM_OPC_i2d = 135, 242 | JVM_OPC_l2i = 136, 243 | JVM_OPC_l2f = 137, 244 | JVM_OPC_l2d = 138, 245 | JVM_OPC_f2i = 139, 246 | JVM_OPC_f2l = 140, 247 | JVM_OPC_f2d = 141, 248 | JVM_OPC_d2i = 142, 249 | JVM_OPC_d2l = 143, 250 | JVM_OPC_d2f = 144, 251 | JVM_OPC_i2b = 145, 252 | JVM_OPC_i2c = 146, 253 | JVM_OPC_i2s = 147, 254 | JVM_OPC_lcmp = 148, 255 | JVM_OPC_fcmpl = 149, 256 | JVM_OPC_fcmpg = 150, 257 | JVM_OPC_dcmpl = 151, 258 | JVM_OPC_dcmpg = 152, 259 | JVM_OPC_ifeq = 153, 260 | JVM_OPC_ifne = 154, 261 | JVM_OPC_iflt = 155, 262 | JVM_OPC_ifge = 156, 263 | JVM_OPC_ifgt = 157, 264 | JVM_OPC_ifle = 158, 265 | JVM_OPC_if_icmpeq = 159, 266 | JVM_OPC_if_icmpne = 160, 267 | JVM_OPC_if_icmplt = 161, 268 | JVM_OPC_if_icmpge = 162, 269 | JVM_OPC_if_icmpgt = 163, 270 | JVM_OPC_if_icmple = 164, 271 | JVM_OPC_if_acmpeq = 165, 272 | JVM_OPC_if_acmpne = 166, 273 | JVM_OPC_goto = 167, 274 | JVM_OPC_jsr = 168, 275 | JVM_OPC_ret = 169, 276 | JVM_OPC_tableswitch = 170, 277 | JVM_OPC_lookupswitch = 171, 278 | JVM_OPC_ireturn = 172, 279 | JVM_OPC_lreturn = 173, 280 | JVM_OPC_freturn = 174, 281 | JVM_OPC_dreturn = 175, 282 | JVM_OPC_areturn = 176, 283 | JVM_OPC_return = 177, 284 | JVM_OPC_getstatic = 178, 285 | JVM_OPC_putstatic = 179, 286 | JVM_OPC_getfield = 180, 287 | JVM_OPC_putfield = 181, 288 | JVM_OPC_invokevirtual = 182, 289 | JVM_OPC_invokespecial = 183, 290 | JVM_OPC_invokestatic = 184, 291 | JVM_OPC_invokeinterface = 185, 292 | JVM_OPC_xxxunusedxxx = 186, 293 | JVM_OPC_new = 187, 294 | JVM_OPC_newarray = 188, 295 | JVM_OPC_anewarray = 189, 296 | JVM_OPC_arraylength = 190, 297 | JVM_OPC_athrow = 191, 298 | JVM_OPC_checkcast = 192, 299 | JVM_OPC_instanceof = 193, 300 | JVM_OPC_monitorenter = 194, 301 | JVM_OPC_monitorexit = 195, 302 | JVM_OPC_wide = 196, 303 | JVM_OPC_multianewarray = 197, 304 | JVM_OPC_ifnull = 198, 305 | JVM_OPC_ifnonnull = 199, 306 | JVM_OPC_goto_w = 200, 307 | JVM_OPC_jsr_w = 201, 308 | JVM_OPC_MAX = 201 309 | }; 310 | 311 | /* Opcode length initializer, use with something like: 312 | * unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER; 313 | */ 314 | #define JVM_OPCODE_LENGTH_INITIALIZER { \ 315 | 1, /* nop */ \ 316 | 1, /* aconst_null */ \ 317 | 1, /* iconst_m1 */ \ 318 | 1, /* iconst_0 */ \ 319 | 1, /* iconst_1 */ \ 320 | 1, /* iconst_2 */ \ 321 | 1, /* iconst_3 */ \ 322 | 1, /* iconst_4 */ \ 323 | 1, /* iconst_5 */ \ 324 | 1, /* lconst_0 */ \ 325 | 1, /* lconst_1 */ \ 326 | 1, /* fconst_0 */ \ 327 | 1, /* fconst_1 */ \ 328 | 1, /* fconst_2 */ \ 329 | 1, /* dconst_0 */ \ 330 | 1, /* dconst_1 */ \ 331 | 2, /* bipush */ \ 332 | 3, /* sipush */ \ 333 | 2, /* ldc */ \ 334 | 3, /* ldc_w */ \ 335 | 3, /* ldc2_w */ \ 336 | 2, /* iload */ \ 337 | 2, /* lload */ \ 338 | 2, /* fload */ \ 339 | 2, /* dload */ \ 340 | 2, /* aload */ \ 341 | 1, /* iload_0 */ \ 342 | 1, /* iload_1 */ \ 343 | 1, /* iload_2 */ \ 344 | 1, /* iload_3 */ \ 345 | 1, /* lload_0 */ \ 346 | 1, /* lload_1 */ \ 347 | 1, /* lload_2 */ \ 348 | 1, /* lload_3 */ \ 349 | 1, /* fload_0 */ \ 350 | 1, /* fload_1 */ \ 351 | 1, /* fload_2 */ \ 352 | 1, /* fload_3 */ \ 353 | 1, /* dload_0 */ \ 354 | 1, /* dload_1 */ \ 355 | 1, /* dload_2 */ \ 356 | 1, /* dload_3 */ \ 357 | 1, /* aload_0 */ \ 358 | 1, /* aload_1 */ \ 359 | 1, /* aload_2 */ \ 360 | 1, /* aload_3 */ \ 361 | 1, /* iaload */ \ 362 | 1, /* laload */ \ 363 | 1, /* faload */ \ 364 | 1, /* daload */ \ 365 | 1, /* aaload */ \ 366 | 1, /* baload */ \ 367 | 1, /* caload */ \ 368 | 1, /* saload */ \ 369 | 2, /* istore */ \ 370 | 2, /* lstore */ \ 371 | 2, /* fstore */ \ 372 | 2, /* dstore */ \ 373 | 2, /* astore */ \ 374 | 1, /* istore_0 */ \ 375 | 1, /* istore_1 */ \ 376 | 1, /* istore_2 */ \ 377 | 1, /* istore_3 */ \ 378 | 1, /* lstore_0 */ \ 379 | 1, /* lstore_1 */ \ 380 | 1, /* lstore_2 */ \ 381 | 1, /* lstore_3 */ \ 382 | 1, /* fstore_0 */ \ 383 | 1, /* fstore_1 */ \ 384 | 1, /* fstore_2 */ \ 385 | 1, /* fstore_3 */ \ 386 | 1, /* dstore_0 */ \ 387 | 1, /* dstore_1 */ \ 388 | 1, /* dstore_2 */ \ 389 | 1, /* dstore_3 */ \ 390 | 1, /* astore_0 */ \ 391 | 1, /* astore_1 */ \ 392 | 1, /* astore_2 */ \ 393 | 1, /* astore_3 */ \ 394 | 1, /* iastore */ \ 395 | 1, /* lastore */ \ 396 | 1, /* fastore */ \ 397 | 1, /* dastore */ \ 398 | 1, /* aastore */ \ 399 | 1, /* bastore */ \ 400 | 1, /* castore */ \ 401 | 1, /* sastore */ \ 402 | 1, /* pop */ \ 403 | 1, /* pop2 */ \ 404 | 1, /* dup */ \ 405 | 1, /* dup_x1 */ \ 406 | 1, /* dup_x2 */ \ 407 | 1, /* dup2 */ \ 408 | 1, /* dup2_x1 */ \ 409 | 1, /* dup2_x2 */ \ 410 | 1, /* swap */ \ 411 | 1, /* iadd */ \ 412 | 1, /* ladd */ \ 413 | 1, /* fadd */ \ 414 | 1, /* dadd */ \ 415 | 1, /* isub */ \ 416 | 1, /* lsub */ \ 417 | 1, /* fsub */ \ 418 | 1, /* dsub */ \ 419 | 1, /* imul */ \ 420 | 1, /* lmul */ \ 421 | 1, /* fmul */ \ 422 | 1, /* dmul */ \ 423 | 1, /* idiv */ \ 424 | 1, /* ldiv */ \ 425 | 1, /* fdiv */ \ 426 | 1, /* ddiv */ \ 427 | 1, /* irem */ \ 428 | 1, /* lrem */ \ 429 | 1, /* frem */ \ 430 | 1, /* drem */ \ 431 | 1, /* ineg */ \ 432 | 1, /* lneg */ \ 433 | 1, /* fneg */ \ 434 | 1, /* dneg */ \ 435 | 1, /* ishl */ \ 436 | 1, /* lshl */ \ 437 | 1, /* ishr */ \ 438 | 1, /* lshr */ \ 439 | 1, /* iushr */ \ 440 | 1, /* lushr */ \ 441 | 1, /* iand */ \ 442 | 1, /* land */ \ 443 | 1, /* ior */ \ 444 | 1, /* lor */ \ 445 | 1, /* ixor */ \ 446 | 1, /* lxor */ \ 447 | 3, /* iinc */ \ 448 | 1, /* i2l */ \ 449 | 1, /* i2f */ \ 450 | 1, /* i2d */ \ 451 | 1, /* l2i */ \ 452 | 1, /* l2f */ \ 453 | 1, /* l2d */ \ 454 | 1, /* f2i */ \ 455 | 1, /* f2l */ \ 456 | 1, /* f2d */ \ 457 | 1, /* d2i */ \ 458 | 1, /* d2l */ \ 459 | 1, /* d2f */ \ 460 | 1, /* i2b */ \ 461 | 1, /* i2c */ \ 462 | 1, /* i2s */ \ 463 | 1, /* lcmp */ \ 464 | 1, /* fcmpl */ \ 465 | 1, /* fcmpg */ \ 466 | 1, /* dcmpl */ \ 467 | 1, /* dcmpg */ \ 468 | 3, /* ifeq */ \ 469 | 3, /* ifne */ \ 470 | 3, /* iflt */ \ 471 | 3, /* ifge */ \ 472 | 3, /* ifgt */ \ 473 | 3, /* ifle */ \ 474 | 3, /* if_icmpeq */ \ 475 | 3, /* if_icmpne */ \ 476 | 3, /* if_icmplt */ \ 477 | 3, /* if_icmpge */ \ 478 | 3, /* if_icmpgt */ \ 479 | 3, /* if_icmple */ \ 480 | 3, /* if_acmpeq */ \ 481 | 3, /* if_acmpne */ \ 482 | 3, /* goto */ \ 483 | 3, /* jsr */ \ 484 | 2, /* ret */ \ 485 | 99, /* tableswitch */ \ 486 | 99, /* lookupswitch */ \ 487 | 1, /* ireturn */ \ 488 | 1, /* lreturn */ \ 489 | 1, /* freturn */ \ 490 | 1, /* dreturn */ \ 491 | 1, /* areturn */ \ 492 | 1, /* return */ \ 493 | 3, /* getstatic */ \ 494 | 3, /* putstatic */ \ 495 | 3, /* getfield */ \ 496 | 3, /* putfield */ \ 497 | 3, /* invokevirtual */ \ 498 | 3, /* invokespecial */ \ 499 | 3, /* invokestatic */ \ 500 | 5, /* invokeinterface */ \ 501 | 0, /* xxxunusedxxx */ \ 502 | 3, /* new */ \ 503 | 2, /* newarray */ \ 504 | 3, /* anewarray */ \ 505 | 1, /* arraylength */ \ 506 | 1, /* athrow */ \ 507 | 3, /* checkcast */ \ 508 | 3, /* instanceof */ \ 509 | 1, /* monitorenter */ \ 510 | 1, /* monitorexit */ \ 511 | 0, /* wide */ \ 512 | 4, /* multianewarray */ \ 513 | 3, /* ifnull */ \ 514 | 3, /* ifnonnull */ \ 515 | 5, /* goto_w */ \ 516 | 5 /* jsr_w */ \ 517 | } 518 | 519 | #ifdef __cplusplus 520 | } /* extern "C" */ 521 | #endif /* __cplusplus */ 522 | 523 | #endif /* CLASSFILE_CONSTANTS */ 524 | -------------------------------------------------------------------------------- /java_crw_demo/README.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # - Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # - Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # - Neither the name of Oracle nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | java_crw_demo Library 33 | 34 | The library java_crw_demo is a small C library that is used by HPROF 35 | and other agent libraries to do some very basic bytecode 36 | insertion (BCI) of class files. This is not an agent 37 | library but a general purpose library that can be used to do some 38 | very limited bytecode insertion. 39 | 40 | In the demo sources, look for the use of java_crw_demo.h and 41 | the C function java_crw_demo(). The java_crw_demo library is provided 42 | as part of the JRE. 43 | 44 | The basic BCI that this library does includes: 45 | 46 | * On entry to the java.lang.Object init method (signature "()V"), 47 | a invokestatic call to tclass.obj_init_method(object); is inserted. 48 | 49 | * On any newarray type opcode, immediately following it, the array 50 | object is duplicated on the stack and an invokestatic call to 51 | tclass.newarray_method(object); is inserted. 52 | 53 | * On entry to all methods, a invokestatic call to 54 | tclass.call_method(cnum,mnum); is inserted. The agent can map the 55 | two integers (cnum,mnum) to a method in a class, the cnum is the 56 | number provided to the java_crw_demo library when the classfile was 57 | modified. 58 | 59 | * On return from any method (any return opcode), a invokestatic call to 60 | tclass.return_method(cnum,mnum); is inserted. 61 | 62 | Some methods are not modified at all, init methods and finalize methods 63 | whose length is 1 will not be modified. Classes that are designated 64 | "system" will not have their clinit methods modified. In addition, the 65 | method java.lang.Thread.currentThread() is not modified. 66 | 67 | No methods or fields will be added to any class, however new constant 68 | pool entries will be added at the end of the original constant pool table. 69 | The exception, line, and local variable tables for each method is adjusted 70 | for the modification. The bytecodes are compressed to use smaller offsets 71 | and the fewest 'wide' opcodes. 72 | 73 | All attempts are made to minimize the number of bytecodes at each insertion 74 | site, however, classes with N return opcodes or N newarray opcodes will get 75 | N insertions. And only the necessary modification dictated by the input 76 | arguments to java_crw_demo are actually made. 77 | 78 | -------------------------------------------------------------------------------- /java_crw_demo/java_crw_demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * - Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * - Neither the name of Oracle nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | /* Class reader writer (java_crw_demo) for instrumenting bytecodes */ 33 | 34 | /* 35 | * As long as the callbacks allow for it and the class number is unique, 36 | * this code is completely re-entrant and any number of classfile 37 | * injections can happen at the same time. 38 | * 39 | * The current logic requires a unique number for this class instance 40 | * or (jclass,jobject loader) pair, this is done via the ClassIndex 41 | * in hprof, which is passed in as the 'unsigned cnum' to java_crw_demo(). 42 | * It's up to the user of this interface if it wants to use this 43 | * feature. 44 | * 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | /* Get Java and class file and bytecode information. */ 52 | 53 | #include 54 | 55 | #include "classfile_constants.h" 56 | 57 | 58 | /* Include our own interface for cross check */ 59 | 60 | #include "java_crw_demo.h" 61 | 62 | /* Macros over error functions to capture line numbers */ 63 | 64 | #define CRW_FATAL(ci, message) fatal_error(ci, message, __FILE__, __LINE__) 65 | 66 | #if defined(DEBUG) || !defined(NDEBUG) 67 | 68 | #define CRW_ASSERT(ci, cond) \ 69 | ((cond)?(void)0:assert_error(ci, #cond, __FILE__, __LINE__)) 70 | 71 | #else 72 | 73 | #define CRW_ASSERT(ci, cond) 74 | 75 | #endif 76 | 77 | #define CRW_ASSERT_MI(mi) CRW_ASSERT((mi)?(mi)->ci:NULL,(mi)!=NULL) 78 | 79 | #define CRW_ASSERT_CI(ci) CRW_ASSERT(ci, ( (ci) != NULL && \ 80 | (ci)->input_position <= (ci)->input_len && \ 81 | (ci)->output_position <= (ci)->output_len) ) 82 | 83 | /* Typedefs for various integral numbers, just for code clarity */ 84 | 85 | typedef unsigned ClassOpcode; /* One opcode */ 86 | typedef unsigned char ByteCode; /* One byte from bytecodes */ 87 | typedef int ByteOffset; /* Byte offset */ 88 | typedef int ClassConstant; /* Constant pool kind */ 89 | typedef long CrwPosition; /* Position in class image */ 90 | typedef unsigned short CrwCpoolIndex; /* Index into constant pool */ 91 | 92 | /* Misc support macros */ 93 | 94 | /* Given the position of an opcode, find the next 4byte boundary position */ 95 | #define NEXT_4BYTE_BOUNDARY(opcode_pos) (((opcode_pos)+4) & (~3)) 96 | 97 | #define LARGEST_INJECTION (12*3) /* 3 injections at same site */ 98 | #define MAXIMUM_NEW_CPOOL_ENTRIES 64 /* don't add more than 32 entries */ 99 | 100 | /* Constant Pool Entry (internal table that mirrors pool in file image) */ 101 | 102 | typedef struct { 103 | const char * ptr; /* Pointer to any string */ 104 | unsigned short len; /* Length of string */ 105 | unsigned int index1; /* 1st 16 bit index or 32bit value. */ 106 | unsigned int index2; /* 2nd 16 bit index or 32bit value. */ 107 | ClassConstant tag; /* Tag or kind of entry. */ 108 | } CrwConstantPoolEntry; 109 | 110 | struct MethodImage; 111 | 112 | /* Class file image storage structure */ 113 | 114 | typedef struct CrwClassImage { 115 | 116 | /* Unique class number for this class */ 117 | unsigned number; 118 | 119 | /* Name of class, given or gotten out of class image */ 120 | const char * name; 121 | 122 | /* Input and Output class images tracking */ 123 | const unsigned char * input; 124 | unsigned char * output; 125 | CrwPosition input_len; 126 | CrwPosition output_len; 127 | CrwPosition input_position; 128 | CrwPosition output_position; 129 | 130 | /* Mirrored constant pool */ 131 | CrwConstantPoolEntry * cpool; 132 | CrwCpoolIndex cpool_max_elements; /* Max count */ 133 | CrwCpoolIndex cpool_count_plus_one; 134 | 135 | /* Input flags about class (e.g. is it a system class) */ 136 | int system_class; 137 | 138 | /* Class access flags gotten from file. */ 139 | unsigned access_flags; 140 | 141 | /* Names of classes and methods. */ 142 | char* tclass_name; /* Name of class that has tracker methods. */ 143 | char* tclass_sig; /* Signature of class */ 144 | char* call_name; /* Method name to call at offset 0 */ 145 | char* call_sig; /* Signature of this method */ 146 | char* return_name; /* Method name to call before any return */ 147 | char* return_sig; /* Signature of this method */ 148 | char* obj_init_name; /* Method name to call in Object */ 149 | char* obj_init_sig; /* Signature of this method */ 150 | char* newarray_name; /* Method name to call after newarray opcodes */ 151 | char* newarray_sig; /* Signature of this method */ 152 | 153 | /* Constant pool index values for new entries */ 154 | CrwCpoolIndex tracker_class_index; 155 | CrwCpoolIndex object_init_tracker_index; 156 | CrwCpoolIndex newarray_tracker_index; 157 | CrwCpoolIndex call_tracker_index; 158 | CrwCpoolIndex return_tracker_index; 159 | CrwCpoolIndex class_number_index; /* Class number in pool */ 160 | 161 | /* Count of injections made into this class */ 162 | int injection_count; 163 | 164 | /* This class must be the java.lang.Object class */ 165 | jboolean is_object_class; 166 | 167 | /* This class must be the java.lang.Thread class */ 168 | jboolean is_thread_class; 169 | 170 | /* Callback functions */ 171 | FatalErrorHandler fatal_error_handler; 172 | MethodNumberRegister mnum_callback; 173 | 174 | /* Table of method names and descr's */ 175 | int method_count; 176 | const char ** method_name; 177 | const char ** method_descr; 178 | struct MethodImage * current_mi; 179 | 180 | } CrwClassImage; 181 | 182 | /* Injection bytecodes (holds injected bytecodes for each code position) */ 183 | 184 | typedef struct { 185 | ByteCode * code; 186 | ByteOffset len; 187 | } Injection; 188 | 189 | /* Method transformation data (allocated/freed as each method is processed) */ 190 | 191 | typedef struct MethodImage { 192 | 193 | /* Back reference to Class image data. */ 194 | CrwClassImage * ci; 195 | 196 | /* Unique method number for this class. */ 197 | unsigned number; 198 | 199 | /* Method name and descr */ 200 | const char * name; 201 | const char * descr; 202 | 203 | /* Map of input bytecode offsets to output bytecode offsets */ 204 | ByteOffset * map; 205 | 206 | /* Bytecode injections for each input bytecode offset */ 207 | Injection * injections; 208 | 209 | /* Widening setting for each input bytecode offset */ 210 | signed char * widening; 211 | 212 | /* Length of original input bytecodes, and new bytecodes. */ 213 | ByteOffset code_len; 214 | ByteOffset new_code_len; 215 | 216 | /* Location in input where bytecodes are located. */ 217 | CrwPosition start_of_input_bytecodes; 218 | 219 | /* Original max_stack and new max stack */ 220 | unsigned max_stack; 221 | unsigned new_max_stack; 222 | 223 | jboolean object_init_method; 224 | jboolean skip_call_return_sites; 225 | 226 | /* Method access flags gotten from file. */ 227 | unsigned access_flags; 228 | 229 | } MethodImage; 230 | 231 | /* ----------------------------------------------------------------- */ 232 | /* General support functions (memory and error handling) */ 233 | 234 | static void 235 | fatal_error(CrwClassImage *ci, const char *message, const char *file, int line) 236 | { 237 | if ( ci != NULL && ci->fatal_error_handler != NULL ) { 238 | (*ci->fatal_error_handler)(message, file, line); 239 | } else { 240 | /* Normal operation should NEVER reach here */ 241 | /* NO CRW FATAL ERROR HANDLER! */ 242 | (void)fprintf(stderr, "CRW: %s [%s:%d]\n", message, file, line); 243 | abort(); 244 | } 245 | } 246 | 247 | #if defined(DEBUG) || !defined(NDEBUG) 248 | static void 249 | assert_error(CrwClassImage *ci, const char *condition, 250 | const char *file, int line) 251 | { 252 | char buf[512]; 253 | MethodImage *mi; 254 | ByteOffset byte_code_offset; 255 | 256 | mi = ci->current_mi; 257 | if ( mi != NULL ) { 258 | byte_code_offset = (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes); 259 | } else { 260 | byte_code_offset=-1; 261 | } 262 | 263 | (void)sprintf(buf, 264 | "CRW ASSERTION FAILURE: %s (%s:%s:%d)", 265 | condition, 266 | ci->name==NULL?"?":ci->name, 267 | (mi==NULL||mi->name==NULL)?"?":mi->name, 268 | byte_code_offset); 269 | fatal_error(ci, buf, file, line); 270 | } 271 | #endif 272 | 273 | static void * 274 | allocate(CrwClassImage *ci, int nbytes) 275 | { 276 | void * ptr; 277 | 278 | if ( nbytes <= 0 ) { 279 | CRW_FATAL(ci, "Cannot allocate <= 0 bytes"); 280 | } 281 | ptr = malloc(nbytes); 282 | if ( ptr == NULL ) { 283 | CRW_FATAL(ci, "Ran out of malloc memory"); 284 | } 285 | return ptr; 286 | } 287 | 288 | static void * 289 | reallocate(CrwClassImage *ci, void *optr, int nbytes) 290 | { 291 | void * ptr; 292 | 293 | if ( optr == NULL ) { 294 | CRW_FATAL(ci, "Cannot deallocate NULL"); 295 | } 296 | if ( nbytes <= 0 ) { 297 | CRW_FATAL(ci, "Cannot reallocate <= 0 bytes"); 298 | } 299 | ptr = realloc(optr, nbytes); 300 | if ( ptr == NULL ) { 301 | CRW_FATAL(ci, "Ran out of malloc memory"); 302 | } 303 | return ptr; 304 | } 305 | 306 | static void * 307 | allocate_clean(CrwClassImage *ci, int nbytes) 308 | { 309 | void * ptr; 310 | 311 | if ( nbytes <= 0 ) { 312 | CRW_FATAL(ci, "Cannot allocate <= 0 bytes"); 313 | } 314 | ptr = calloc(nbytes, 1); 315 | if ( ptr == NULL ) { 316 | CRW_FATAL(ci, "Ran out of malloc memory"); 317 | } 318 | return ptr; 319 | } 320 | 321 | static const char * 322 | duplicate(CrwClassImage *ci, const char *str, int len) 323 | { 324 | char *copy; 325 | 326 | copy = (char*)allocate(ci, len+1); 327 | (void)memcpy(copy, str, len); 328 | copy[len] = 0; 329 | return (const char *)copy; 330 | } 331 | 332 | static void 333 | deallocate(CrwClassImage *ci, void *ptr) 334 | { 335 | if ( ptr == NULL ) { 336 | CRW_FATAL(ci, "Cannot deallocate NULL"); 337 | } 338 | (void)free(ptr); 339 | } 340 | 341 | /* ----------------------------------------------------------------- */ 342 | /* Functions for reading/writing bytes to/from the class images */ 343 | 344 | static unsigned 345 | readU1(CrwClassImage *ci) 346 | { 347 | CRW_ASSERT_CI(ci); 348 | return ((unsigned)(ci->input[ci->input_position++])) & 0xFF; 349 | } 350 | 351 | static unsigned 352 | readU2(CrwClassImage *ci) 353 | { 354 | unsigned res; 355 | 356 | res = readU1(ci); 357 | return (res << 8) + readU1(ci); 358 | } 359 | 360 | static signed short 361 | readS2(CrwClassImage *ci) 362 | { 363 | unsigned res; 364 | 365 | res = readU1(ci); 366 | return ((res << 8) + readU1(ci)) & 0xFFFF; 367 | } 368 | 369 | static unsigned 370 | readU4(CrwClassImage *ci) 371 | { 372 | unsigned res; 373 | 374 | res = readU2(ci); 375 | return (res << 16) + readU2(ci); 376 | } 377 | 378 | static void 379 | writeU1(CrwClassImage *ci, unsigned val) /* Only writes out lower 8 bits */ 380 | { 381 | CRW_ASSERT_CI(ci); 382 | if ( ci->output != NULL ) { 383 | ci->output[ci->output_position++] = val & 0xFF; 384 | } 385 | } 386 | 387 | static void 388 | writeU2(CrwClassImage *ci, unsigned val) 389 | { 390 | writeU1(ci, val >> 8); 391 | writeU1(ci, val); 392 | } 393 | 394 | static void 395 | writeU4(CrwClassImage *ci, unsigned val) 396 | { 397 | writeU2(ci, val >> 16); 398 | writeU2(ci, val); 399 | } 400 | 401 | static unsigned 402 | copyU1(CrwClassImage *ci) 403 | { 404 | unsigned value; 405 | 406 | value = readU1(ci); 407 | writeU1(ci, value); 408 | return value; 409 | } 410 | 411 | static unsigned 412 | copyU2(CrwClassImage *ci) 413 | { 414 | unsigned value; 415 | 416 | value = readU2(ci); 417 | writeU2(ci, value); 418 | return value; 419 | } 420 | 421 | static unsigned 422 | copyU4(CrwClassImage *ci) 423 | { 424 | unsigned value; 425 | 426 | value = readU4(ci); 427 | writeU4(ci, value); 428 | return value; 429 | } 430 | 431 | static void 432 | copy(CrwClassImage *ci, unsigned count) 433 | { 434 | CRW_ASSERT_CI(ci); 435 | if ( ci->output != NULL ) { 436 | (void)memcpy(ci->output+ci->output_position, 437 | ci->input+ci->input_position, count); 438 | ci->output_position += count; 439 | } 440 | ci->input_position += count; 441 | CRW_ASSERT_CI(ci); 442 | } 443 | 444 | static void 445 | skip(CrwClassImage *ci, unsigned count) 446 | { 447 | CRW_ASSERT_CI(ci); 448 | ci->input_position += count; 449 | } 450 | 451 | static void 452 | read_bytes(CrwClassImage *ci, void *bytes, unsigned count) 453 | { 454 | CRW_ASSERT_CI(ci); 455 | CRW_ASSERT(ci, bytes!=NULL); 456 | (void)memcpy(bytes, ci->input+ci->input_position, count); 457 | ci->input_position += count; 458 | } 459 | 460 | static void 461 | write_bytes(CrwClassImage *ci, void *bytes, unsigned count) 462 | { 463 | CRW_ASSERT_CI(ci); 464 | CRW_ASSERT(ci, bytes!=NULL); 465 | if ( ci->output != NULL ) { 466 | (void)memcpy(ci->output+ci->output_position, bytes, count); 467 | ci->output_position += count; 468 | } 469 | } 470 | 471 | static void 472 | random_writeU2(CrwClassImage *ci, CrwPosition pos, unsigned val) 473 | { 474 | CrwPosition save_position; 475 | 476 | CRW_ASSERT_CI(ci); 477 | save_position = ci->output_position; 478 | ci->output_position = pos; 479 | writeU2(ci, val); 480 | ci->output_position = save_position; 481 | } 482 | 483 | static void 484 | random_writeU4(CrwClassImage *ci, CrwPosition pos, unsigned val) 485 | { 486 | CrwPosition save_position; 487 | 488 | CRW_ASSERT_CI(ci); 489 | save_position = ci->output_position; 490 | ci->output_position = pos; 491 | writeU4(ci, val); 492 | ci->output_position = save_position; 493 | } 494 | 495 | /* ----------------------------------------------------------------- */ 496 | /* Constant Pool handling functions. */ 497 | 498 | static void 499 | fillin_cpool_entry(CrwClassImage *ci, CrwCpoolIndex i, 500 | ClassConstant tag, 501 | unsigned int index1, unsigned int index2, 502 | const char *ptr, int len) 503 | { 504 | CRW_ASSERT_CI(ci); 505 | CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one); 506 | ci->cpool[i].tag = tag; 507 | ci->cpool[i].index1 = index1; 508 | ci->cpool[i].index2 = index2; 509 | ci->cpool[i].ptr = ptr; 510 | ci->cpool[i].len = (unsigned short)len; 511 | } 512 | 513 | static CrwCpoolIndex 514 | add_new_cpool_entry(CrwClassImage *ci, ClassConstant tag, 515 | unsigned int index1, unsigned int index2, 516 | const char *str, int len) 517 | { 518 | CrwCpoolIndex i; 519 | char *utf8 = NULL; 520 | 521 | CRW_ASSERT_CI(ci); 522 | i = ci->cpool_count_plus_one++; 523 | 524 | /* NOTE: This implementation does not automatically expand the 525 | * constant pool table beyond the expected number needed 526 | * to handle this particular CrwTrackerInterface injections. 527 | * See MAXIMUM_NEW_CPOOL_ENTRIES 528 | */ 529 | CRW_ASSERT(ci, ci->cpool_count_plus_one < ci->cpool_max_elements ); 530 | 531 | writeU1(ci, tag); 532 | switch (tag) { 533 | case JVM_CONSTANT_Class: 534 | writeU2(ci, index1); 535 | break; 536 | case JVM_CONSTANT_String: 537 | writeU2(ci, index1); 538 | break; 539 | case JVM_CONSTANT_Fieldref: 540 | case JVM_CONSTANT_Methodref: 541 | case JVM_CONSTANT_InterfaceMethodref: 542 | case JVM_CONSTANT_Integer: 543 | case JVM_CONSTANT_Float: 544 | case JVM_CONSTANT_NameAndType: 545 | writeU2(ci, index1); 546 | writeU2(ci, index2); 547 | break; 548 | case JVM_CONSTANT_Long: 549 | case JVM_CONSTANT_Double: 550 | writeU4(ci, index1); 551 | writeU4(ci, index2); 552 | ci->cpool_count_plus_one++; 553 | CRW_ASSERT(ci, ci->cpool_count_plus_one < ci->cpool_max_elements ); 554 | break; 555 | case JVM_CONSTANT_Utf8: 556 | CRW_ASSERT(ci, len==(len & 0xFFFF)); 557 | writeU2(ci, len); 558 | write_bytes(ci, (void*)str, len); 559 | utf8 = (char*)duplicate(ci, str, len); 560 | break; 561 | default: 562 | CRW_FATAL(ci, "Unknown constant"); 563 | break; 564 | } 565 | fillin_cpool_entry(ci, i, tag, index1, index2, (const char *)utf8, len); 566 | CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one); 567 | return i; 568 | } 569 | 570 | static CrwCpoolIndex 571 | add_new_class_cpool_entry(CrwClassImage *ci, const char *class_name) 572 | { 573 | CrwCpoolIndex name_index; 574 | CrwCpoolIndex class_index; 575 | int len; 576 | 577 | CRW_ASSERT_CI(ci); 578 | CRW_ASSERT(ci, class_name!=NULL); 579 | 580 | len = (int)strlen(class_name); 581 | name_index = add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, 582 | class_name, len); 583 | class_index = add_new_cpool_entry(ci, JVM_CONSTANT_Class, name_index, 0, 584 | NULL, 0); 585 | return class_index; 586 | } 587 | 588 | static CrwCpoolIndex 589 | add_new_method_cpool_entry(CrwClassImage *ci, CrwCpoolIndex class_index, 590 | const char *name, const char *descr) 591 | { 592 | CrwCpoolIndex name_index; 593 | CrwCpoolIndex descr_index; 594 | CrwCpoolIndex name_type_index; 595 | int len; 596 | 597 | CRW_ASSERT_CI(ci); 598 | CRW_ASSERT(ci, name!=NULL); 599 | CRW_ASSERT(ci, descr!=NULL); 600 | len = (int)strlen(name); 601 | name_index = 602 | add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, name, len); 603 | len = (int)strlen(descr); 604 | descr_index = 605 | add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, descr, len); 606 | name_type_index = 607 | add_new_cpool_entry(ci, JVM_CONSTANT_NameAndType, 608 | name_index, descr_index, NULL, 0); 609 | return add_new_cpool_entry(ci, JVM_CONSTANT_Methodref, 610 | class_index, name_type_index, NULL, 0); 611 | } 612 | 613 | static CrwConstantPoolEntry 614 | cpool_entry(CrwClassImage *ci, CrwCpoolIndex c_index) 615 | { 616 | CRW_ASSERT_CI(ci); 617 | CRW_ASSERT(ci, c_index > 0 && c_index < ci->cpool_count_plus_one); 618 | return ci->cpool[c_index]; 619 | } 620 | 621 | static void 622 | cpool_setup(CrwClassImage *ci) 623 | { 624 | CrwCpoolIndex i; 625 | CrwPosition cpool_output_position; 626 | int count_plus_one; 627 | 628 | CRW_ASSERT_CI(ci); 629 | cpool_output_position = ci->output_position; 630 | count_plus_one = copyU2(ci); 631 | CRW_ASSERT(ci, count_plus_one>1); 632 | ci->cpool_max_elements = count_plus_one+MAXIMUM_NEW_CPOOL_ENTRIES; 633 | ci->cpool = (CrwConstantPoolEntry*)allocate_clean(ci, 634 | (int)((ci->cpool_max_elements)*sizeof(CrwConstantPoolEntry))); 635 | ci->cpool_count_plus_one = (CrwCpoolIndex)count_plus_one; 636 | 637 | /* Index zero not in class file */ 638 | for (i = 1; i < count_plus_one; ++i) { 639 | CrwCpoolIndex ipos; 640 | ClassConstant tag; 641 | unsigned int index1; 642 | unsigned int index2; 643 | unsigned len; 644 | char * utf8; 645 | 646 | ipos = i; 647 | index1 = 0; 648 | index2 = 0; 649 | len = 0; 650 | utf8 = NULL; 651 | 652 | tag = copyU1(ci); 653 | switch (tag) { 654 | case JVM_CONSTANT_Class: 655 | index1 = copyU2(ci); 656 | break; 657 | case JVM_CONSTANT_String: 658 | index1 = copyU2(ci); 659 | break; 660 | case JVM_CONSTANT_Fieldref: 661 | case JVM_CONSTANT_Methodref: 662 | case JVM_CONSTANT_InterfaceMethodref: 663 | case JVM_CONSTANT_Integer: 664 | case JVM_CONSTANT_Float: 665 | case JVM_CONSTANT_NameAndType: 666 | index1 = copyU2(ci); 667 | index2 = copyU2(ci); 668 | break; 669 | case JVM_CONSTANT_Long: 670 | case JVM_CONSTANT_Double: 671 | index1 = copyU4(ci); 672 | index2 = copyU4(ci); 673 | ++i; /* // these take two CP entries - duh! */ 674 | break; 675 | case JVM_CONSTANT_Utf8: 676 | len = copyU2(ci); 677 | index1 = (unsigned short)len; 678 | utf8 = (char*)allocate(ci, len+1); 679 | read_bytes(ci, (void*)utf8, len); 680 | utf8[len] = 0; 681 | write_bytes(ci, (void*)utf8, len); 682 | break; 683 | default: 684 | CRW_FATAL(ci, "Unknown constant"); 685 | break; 686 | } 687 | fillin_cpool_entry(ci, ipos, tag, index1, index2, (const char *)utf8, len); 688 | } 689 | 690 | if (ci->call_name != NULL || ci->return_name != NULL) { 691 | if ( ci->number != (ci->number & 0x7FFF) ) { 692 | ci->class_number_index = 693 | add_new_cpool_entry(ci, JVM_CONSTANT_Integer, 694 | (ci->number>>16) & 0xFFFF, ci->number & 0xFFFF, NULL, 0); 695 | } 696 | } 697 | 698 | if ( ci->tclass_name != NULL ) { 699 | ci->tracker_class_index = 700 | add_new_class_cpool_entry(ci, ci->tclass_name); 701 | } 702 | if (ci->obj_init_name != NULL) { 703 | ci->object_init_tracker_index = add_new_method_cpool_entry(ci, 704 | ci->tracker_class_index, 705 | ci->obj_init_name, 706 | ci->obj_init_sig); 707 | } 708 | if (ci->newarray_name != NULL) { 709 | ci->newarray_tracker_index = add_new_method_cpool_entry(ci, 710 | ci->tracker_class_index, 711 | ci->newarray_name, 712 | ci->newarray_sig); 713 | } 714 | if (ci->call_name != NULL) { 715 | ci->call_tracker_index = add_new_method_cpool_entry(ci, 716 | ci->tracker_class_index, 717 | ci->call_name, 718 | ci->call_sig); 719 | } 720 | if (ci->return_name != NULL) { 721 | ci->return_tracker_index = add_new_method_cpool_entry(ci, 722 | ci->tracker_class_index, 723 | ci->return_name, 724 | ci->return_sig); 725 | } 726 | 727 | random_writeU2(ci, cpool_output_position, ci->cpool_count_plus_one); 728 | } 729 | 730 | /* ----------------------------------------------------------------- */ 731 | /* Functions that create the bytecodes to inject */ 732 | 733 | static ByteOffset 734 | push_pool_constant_bytecodes(ByteCode *bytecodes, CrwCpoolIndex index) 735 | { 736 | ByteOffset nbytes = 0; 737 | 738 | if ( index == (index&0x7F) ) { 739 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc; 740 | } else { 741 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc_w; 742 | bytecodes[nbytes++] = (ByteCode)((index >> 8) & 0xFF); 743 | } 744 | bytecodes[nbytes++] = (ByteCode)(index & 0xFF); 745 | return nbytes; 746 | } 747 | 748 | static ByteOffset 749 | push_short_constant_bytecodes(ByteCode *bytecodes, unsigned number) 750 | { 751 | ByteOffset nbytes = 0; 752 | 753 | if ( number <= 5 ) { 754 | bytecodes[nbytes++] = (ByteCode)(JVM_OPC_iconst_0+number); 755 | } else if ( number == (number&0x7F) ) { 756 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_bipush; 757 | bytecodes[nbytes++] = (ByteCode)(number & 0xFF); 758 | } else { 759 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_sipush; 760 | bytecodes[nbytes++] = (ByteCode)((number >> 8) & 0xFF); 761 | bytecodes[nbytes++] = (ByteCode)(number & 0xFF); 762 | } 763 | return nbytes; 764 | } 765 | 766 | static ByteOffset 767 | injection_template(MethodImage *mi, ByteCode *bytecodes, ByteOffset max_nbytes, 768 | CrwCpoolIndex method_index) 769 | { 770 | CrwClassImage * ci; 771 | ByteOffset nbytes = 0; 772 | unsigned max_stack; 773 | int add_dup; 774 | int add_aload; 775 | int push_cnum; 776 | int push_mnum; 777 | 778 | ci = mi->ci; 779 | 780 | CRW_ASSERT(ci, bytecodes!=NULL); 781 | 782 | if ( method_index == 0 ) { 783 | return 0; 784 | } 785 | 786 | if ( method_index == ci->newarray_tracker_index) { 787 | max_stack = mi->max_stack + 1; 788 | add_dup = JNI_TRUE; 789 | add_aload = JNI_FALSE; 790 | push_cnum = JNI_FALSE; 791 | push_mnum = JNI_FALSE; 792 | } else if ( method_index == ci->object_init_tracker_index) { 793 | max_stack = mi->max_stack + 1; 794 | add_dup = JNI_FALSE; 795 | add_aload = JNI_TRUE; 796 | push_cnum = JNI_FALSE; 797 | push_mnum = JNI_FALSE; 798 | } else { 799 | max_stack = mi->max_stack + 2; 800 | add_dup = JNI_FALSE; 801 | add_aload = JNI_FALSE; 802 | push_cnum = JNI_TRUE; 803 | push_mnum = JNI_TRUE; 804 | } 805 | 806 | if ( add_dup ) { 807 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_dup; 808 | } 809 | if ( add_aload ) { 810 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_aload_0; 811 | } 812 | if ( push_cnum ) { 813 | if ( ci->number == (ci->number & 0x7FFF) ) { 814 | nbytes += push_short_constant_bytecodes(bytecodes+nbytes, 815 | ci->number); 816 | } else { 817 | CRW_ASSERT(ci, ci->class_number_index!=0); 818 | nbytes += push_pool_constant_bytecodes(bytecodes+nbytes, 819 | ci->class_number_index); 820 | } 821 | } 822 | if ( push_mnum ) { 823 | nbytes += push_short_constant_bytecodes(bytecodes+nbytes, 824 | mi->number); 825 | } 826 | bytecodes[nbytes++] = (ByteCode)JVM_OPC_invokestatic; 827 | bytecodes[nbytes++] = (ByteCode)(method_index >> 8); 828 | bytecodes[nbytes++] = (ByteCode)method_index; 829 | bytecodes[nbytes] = 0; 830 | CRW_ASSERT(ci, nbytes mi->new_max_stack ) { 834 | mi->new_max_stack = max_stack; 835 | } 836 | return nbytes; 837 | } 838 | 839 | /* Called to create injection code at entry to a method */ 840 | static ByteOffset 841 | entry_injection_code(MethodImage *mi, ByteCode *bytecodes, ByteOffset len) 842 | { 843 | CrwClassImage * ci; 844 | ByteOffset nbytes = 0; 845 | 846 | CRW_ASSERT_MI(mi); 847 | 848 | ci = mi->ci; 849 | 850 | if ( mi->object_init_method ) { 851 | nbytes = injection_template(mi, 852 | bytecodes, len, ci->object_init_tracker_index); 853 | } 854 | if ( !mi->skip_call_return_sites ) { 855 | nbytes += injection_template(mi, 856 | bytecodes+nbytes, len-nbytes, ci->call_tracker_index); 857 | } 858 | return nbytes; 859 | } 860 | 861 | /* Called to create injection code before an opcode */ 862 | static ByteOffset 863 | before_injection_code(MethodImage *mi, ClassOpcode opcode, 864 | ByteCode *bytecodes, ByteOffset len) 865 | { 866 | ByteOffset nbytes = 0; 867 | 868 | 869 | CRW_ASSERT_MI(mi); 870 | switch ( opcode ) { 871 | case JVM_OPC_return: 872 | case JVM_OPC_ireturn: 873 | case JVM_OPC_lreturn: 874 | case JVM_OPC_freturn: 875 | case JVM_OPC_dreturn: 876 | case JVM_OPC_areturn: 877 | if ( !mi->skip_call_return_sites ) { 878 | nbytes = injection_template(mi, 879 | bytecodes, len, mi->ci->return_tracker_index); 880 | } 881 | break; 882 | default: 883 | break; 884 | } 885 | return nbytes; 886 | } 887 | 888 | /* Called to create injection code after an opcode */ 889 | static ByteOffset 890 | after_injection_code(MethodImage *mi, ClassOpcode opcode, 891 | ByteCode *bytecodes, ByteOffset len) 892 | { 893 | CrwClassImage* ci; 894 | ByteOffset nbytes; 895 | 896 | ci = mi->ci; 897 | nbytes = 0; 898 | 899 | CRW_ASSERT_MI(mi); 900 | switch ( opcode ) { 901 | case JVM_OPC_new: 902 | /* Can't inject here cannot pass around uninitialized object */ 903 | break; 904 | case JVM_OPC_newarray: 905 | case JVM_OPC_anewarray: 906 | case JVM_OPC_multianewarray: 907 | nbytes = injection_template(mi, 908 | bytecodes, len, ci->newarray_tracker_index); 909 | break; 910 | default: 911 | break; 912 | } 913 | return nbytes; 914 | } 915 | 916 | /* Actually inject the bytecodes */ 917 | static void 918 | inject_bytecodes(MethodImage *mi, ByteOffset at, 919 | ByteCode *bytecodes, ByteOffset len) 920 | { 921 | Injection injection; 922 | CrwClassImage *ci; 923 | 924 | ci = mi->ci; 925 | CRW_ASSERT_MI(mi); 926 | CRW_ASSERT(ci, at <= mi->code_len); 927 | 928 | injection = mi->injections[at]; 929 | 930 | CRW_ASSERT(ci, len <= LARGEST_INJECTION/2); 931 | CRW_ASSERT(ci, injection.len+len <= LARGEST_INJECTION); 932 | 933 | /* Either start an injection area or concatenate to what is there */ 934 | if ( injection.code == NULL ) { 935 | CRW_ASSERT(ci, injection.len==0); 936 | injection.code = (ByteCode *)allocate_clean(ci, LARGEST_INJECTION+1); 937 | } 938 | 939 | (void)memcpy(injection.code+injection.len, bytecodes, len); 940 | injection.len += len; 941 | injection.code[injection.len] = 0; 942 | mi->injections[at] = injection; 943 | ci->injection_count++; 944 | } 945 | 946 | /* ----------------------------------------------------------------- */ 947 | /* Method handling functions */ 948 | 949 | static MethodImage * 950 | method_init(CrwClassImage *ci, unsigned mnum, ByteOffset code_len) 951 | { 952 | MethodImage * mi; 953 | ByteOffset i; 954 | 955 | mi = (MethodImage*)allocate_clean(ci, (int)sizeof(MethodImage)); 956 | mi->ci = ci; 957 | mi->name = ci->method_name[mnum]; 958 | mi->descr = ci->method_descr[mnum]; 959 | mi->code_len = code_len; 960 | mi->map = (ByteOffset*)allocate_clean(ci, 961 | (int)((code_len+1)*sizeof(ByteOffset))); 962 | for(i=0; i<=code_len; i++) { 963 | mi->map[i] = i; 964 | } 965 | mi->widening = (signed char*)allocate_clean(ci, code_len+1); 966 | mi->injections = (Injection *)allocate_clean(ci, 967 | (int)((code_len+1)*sizeof(Injection))); 968 | mi->number = mnum; 969 | ci->current_mi = mi; 970 | return mi; 971 | } 972 | 973 | static void 974 | method_term(MethodImage *mi) 975 | { 976 | CrwClassImage *ci; 977 | 978 | ci = mi->ci; 979 | CRW_ASSERT_MI(mi); 980 | if ( mi->map != NULL ) { 981 | deallocate(ci, (void*)mi->map); 982 | mi->map = NULL; 983 | } 984 | if ( mi->widening != NULL ) { 985 | deallocate(ci, (void*)mi->widening); 986 | mi->widening = NULL; 987 | } 988 | if ( mi->injections != NULL ) { 989 | ByteOffset i; 990 | for(i=0; i<= mi->code_len; i++) { 991 | if ( mi->injections[i].code != NULL ) { 992 | deallocate(ci, (void*)mi->injections[i].code); 993 | mi->injections[i].code = NULL; 994 | } 995 | } 996 | deallocate(ci, (void*)mi->injections); 997 | mi->injections = NULL; 998 | } 999 | ci->current_mi = NULL; 1000 | deallocate(ci, (void*)mi); 1001 | } 1002 | 1003 | static ByteOffset 1004 | input_code_offset(MethodImage *mi) 1005 | { 1006 | CRW_ASSERT_MI(mi); 1007 | return (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes); 1008 | } 1009 | 1010 | static void 1011 | rewind_to_beginning_of_input_bytecodes(MethodImage *mi) 1012 | { 1013 | CRW_ASSERT_MI(mi); 1014 | mi->ci->input_position = mi->start_of_input_bytecodes; 1015 | } 1016 | 1017 | /* Starting at original byte position 'at', add 'offset' to it's new 1018 | * location. This may be a negative value. 1019 | * NOTE: That this map is not the new bytecode location of the opcode 1020 | * but the new bytecode location that should be used when 1021 | * a goto or jump instruction was targeting the old bytecode 1022 | * location. 1023 | */ 1024 | static void 1025 | adjust_map(MethodImage *mi, ByteOffset at, ByteOffset offset) 1026 | { 1027 | ByteOffset i; 1028 | 1029 | CRW_ASSERT_MI(mi); 1030 | for (i = at; i <= mi->code_len; ++i) { 1031 | mi->map[i] += offset; 1032 | } 1033 | } 1034 | 1035 | static void 1036 | widen(MethodImage *mi, ByteOffset at, ByteOffset len) 1037 | { 1038 | int delta; 1039 | 1040 | CRW_ASSERT(mi->ci, at <= mi->code_len); 1041 | delta = len - mi->widening[at]; 1042 | /* Adjust everything from the current input location by delta */ 1043 | adjust_map(mi, input_code_offset(mi), delta); 1044 | /* Mark at beginning of instruction */ 1045 | mi->widening[at] = (signed char)len; 1046 | } 1047 | 1048 | static void 1049 | verify_opc_wide(CrwClassImage *ci, ClassOpcode wopcode) 1050 | { 1051 | switch (wopcode) { 1052 | case JVM_OPC_aload: case JVM_OPC_astore: 1053 | case JVM_OPC_fload: case JVM_OPC_fstore: 1054 | case JVM_OPC_iload: case JVM_OPC_istore: 1055 | case JVM_OPC_lload: case JVM_OPC_lstore: 1056 | case JVM_OPC_dload: case JVM_OPC_dstore: 1057 | case JVM_OPC_ret: case JVM_OPC_iinc: 1058 | break; 1059 | default: 1060 | CRW_FATAL(ci, "Invalid opcode supplied to wide opcode"); 1061 | break; 1062 | } 1063 | } 1064 | 1065 | static unsigned 1066 | opcode_length(CrwClassImage *ci, ClassOpcode opcode) 1067 | { 1068 | /* Define array that holds length of an opcode */ 1069 | static unsigned char _opcode_length[JVM_OPC_MAX+1] = 1070 | JVM_OPCODE_LENGTH_INITIALIZER; 1071 | 1072 | if ( opcode > JVM_OPC_MAX ) { 1073 | CRW_FATAL(ci, "Invalid opcode supplied to opcode_length()"); 1074 | } 1075 | return _opcode_length[opcode]; 1076 | } 1077 | 1078 | /* Walk one instruction and inject instrumentation */ 1079 | static void 1080 | inject_for_opcode(MethodImage *mi) 1081 | { 1082 | CrwClassImage * ci; 1083 | ClassOpcode opcode; 1084 | int pos; 1085 | 1086 | CRW_ASSERT_MI(mi); 1087 | ci = mi->ci; 1088 | pos = input_code_offset(mi); 1089 | opcode = readU1(ci); 1090 | 1091 | if (opcode == JVM_OPC_wide) { 1092 | ClassOpcode wopcode; 1093 | 1094 | wopcode = readU1(ci); 1095 | /* lvIndex not used */ 1096 | (void)readU2(ci); 1097 | verify_opc_wide(ci, wopcode); 1098 | if ( wopcode==JVM_OPC_iinc ) { 1099 | (void)readU1(ci); 1100 | (void)readU1(ci); 1101 | } 1102 | } else { 1103 | 1104 | ByteCode bytecodes[LARGEST_INJECTION+1]; 1105 | int header; 1106 | int instr_len; 1107 | int low; 1108 | int high; 1109 | int npairs; 1110 | ByteOffset len; 1111 | 1112 | /* Get bytecodes to inject before this opcode */ 1113 | len = before_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes)); 1114 | if ( len > 0 ) { 1115 | inject_bytecodes(mi, pos, bytecodes, len); 1116 | /* Adjust map after processing this opcode */ 1117 | } 1118 | 1119 | /* Process this opcode */ 1120 | switch (opcode) { 1121 | case JVM_OPC_tableswitch: 1122 | header = NEXT_4BYTE_BOUNDARY(pos); 1123 | skip(ci, header - (pos+1)); 1124 | (void)readU4(ci); 1125 | low = readU4(ci); 1126 | high = readU4(ci); 1127 | skip(ci, (high+1-low) * 4); 1128 | break; 1129 | case JVM_OPC_lookupswitch: 1130 | header = NEXT_4BYTE_BOUNDARY(pos); 1131 | skip(ci, header - (pos+1)); 1132 | (void)readU4(ci); 1133 | npairs = readU4(ci); 1134 | skip(ci, npairs * 8); 1135 | break; 1136 | default: 1137 | instr_len = opcode_length(ci, opcode); 1138 | skip(ci, instr_len-1); 1139 | break; 1140 | } 1141 | 1142 | /* Get position after this opcode is processed */ 1143 | pos = input_code_offset(mi); 1144 | 1145 | /* Adjust for any before_injection_code() */ 1146 | if ( len > 0 ) { 1147 | /* Adjust everything past this opcode. 1148 | * Why past it? Because we want any jumps to this bytecode loc 1149 | * to go to the injected code, not where the opcode 1150 | * was moved too. 1151 | * Consider a 'return' opcode that is jumped too. 1152 | * NOTE: This may not be correct in all cases, but will 1153 | * when we are only dealing with non-variable opcodes 1154 | * like the return opcodes. Be careful if the 1155 | * before_injection_code() changes to include other 1156 | * opcodes that have variable length. 1157 | */ 1158 | adjust_map(mi, pos, len); 1159 | } 1160 | 1161 | /* Get bytecodes to inject after this opcode */ 1162 | len = after_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes)); 1163 | if ( len > 0 ) { 1164 | inject_bytecodes(mi, pos, bytecodes, len); 1165 | 1166 | /* Adjust for any after_injection_code() */ 1167 | adjust_map(mi, pos, len); 1168 | } 1169 | 1170 | } 1171 | } 1172 | 1173 | /* Map original bytecode location to it's new location. (See adjust_map()). */ 1174 | static ByteOffset 1175 | method_code_map(MethodImage *mi, ByteOffset pos) 1176 | { 1177 | CRW_ASSERT_MI(mi); 1178 | CRW_ASSERT(mi->ci, pos <= mi->code_len); 1179 | return mi->map[pos]; 1180 | } 1181 | 1182 | static int 1183 | adjust_instruction(MethodImage *mi) 1184 | { 1185 | CrwClassImage * ci; 1186 | ClassOpcode opcode; 1187 | int pos; 1188 | int new_pos; 1189 | 1190 | CRW_ASSERT_MI(mi); 1191 | ci = mi->ci; 1192 | pos = input_code_offset(mi); 1193 | new_pos = method_code_map(mi,pos); 1194 | 1195 | opcode = readU1(ci); 1196 | 1197 | if (opcode == JVM_OPC_wide) { 1198 | ClassOpcode wopcode; 1199 | 1200 | wopcode = readU1(ci); 1201 | /* lvIndex not used */ 1202 | (void)readU2(ci); 1203 | verify_opc_wide(ci, wopcode); 1204 | if ( wopcode==JVM_OPC_iinc ) { 1205 | (void)readU1(ci); 1206 | (void)readU1(ci); 1207 | } 1208 | } else { 1209 | 1210 | int widened; 1211 | int header; 1212 | int newHeader; 1213 | int low; 1214 | int high; 1215 | int new_pad; 1216 | int old_pad; 1217 | int delta; 1218 | int new_delta; 1219 | int delta_pad; 1220 | int npairs; 1221 | int instr_len; 1222 | 1223 | switch (opcode) { 1224 | 1225 | case JVM_OPC_tableswitch: 1226 | widened = mi->widening[pos]; 1227 | header = NEXT_4BYTE_BOUNDARY(pos); 1228 | newHeader = NEXT_4BYTE_BOUNDARY(new_pos); 1229 | 1230 | skip(ci, header - (pos+1)); 1231 | 1232 | delta = readU4(ci); 1233 | low = readU4(ci); 1234 | high = readU4(ci); 1235 | skip(ci, (high+1-low) * 4); 1236 | new_pad = newHeader - new_pos; 1237 | old_pad = header - pos; 1238 | delta_pad = new_pad - old_pad; 1239 | if (widened != delta_pad) { 1240 | widen(mi, pos, delta_pad); 1241 | return 0; 1242 | } 1243 | break; 1244 | 1245 | case JVM_OPC_lookupswitch: 1246 | widened = mi->widening[pos]; 1247 | header = NEXT_4BYTE_BOUNDARY(pos); 1248 | newHeader = NEXT_4BYTE_BOUNDARY(new_pos); 1249 | 1250 | skip(ci, header - (pos+1)); 1251 | 1252 | delta = readU4(ci); 1253 | npairs = readU4(ci); 1254 | skip(ci, npairs * 8); 1255 | new_pad = newHeader - new_pos; 1256 | old_pad = header - pos; 1257 | delta_pad = new_pad - old_pad; 1258 | if (widened != delta_pad) { 1259 | widen(mi, pos, delta_pad); 1260 | return 0; 1261 | } 1262 | break; 1263 | 1264 | case JVM_OPC_jsr: case JVM_OPC_goto: 1265 | case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt: 1266 | case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne: 1267 | case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge: 1268 | case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt: 1269 | case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne: 1270 | case JVM_OPC_ifnull: case JVM_OPC_ifnonnull: 1271 | widened = mi->widening[pos]; 1272 | delta = readS2(ci); 1273 | if (widened == 0) { 1274 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1275 | if ((new_delta < -32768) || (new_delta > 32767)) { 1276 | switch (opcode) { 1277 | case JVM_OPC_jsr: case JVM_OPC_goto: 1278 | widen(mi, pos, 2); 1279 | break; 1280 | default: 1281 | widen(mi, pos, 5); 1282 | break; 1283 | } 1284 | return 0; 1285 | } 1286 | } 1287 | break; 1288 | 1289 | case JVM_OPC_jsr_w: 1290 | case JVM_OPC_goto_w: 1291 | (void)readU4(ci); 1292 | break; 1293 | 1294 | default: 1295 | instr_len = opcode_length(ci, opcode); 1296 | skip(ci, instr_len-1); 1297 | break; 1298 | } 1299 | } 1300 | return 1; 1301 | } 1302 | 1303 | static void 1304 | write_instruction(MethodImage *mi) 1305 | { 1306 | CrwClassImage * ci; 1307 | ClassOpcode opcode; 1308 | ByteOffset new_code_len; 1309 | int pos; 1310 | int new_pos; 1311 | 1312 | CRW_ASSERT_MI(mi); 1313 | ci = mi->ci; 1314 | pos = input_code_offset(mi); 1315 | new_pos = method_code_map(mi,pos); 1316 | new_code_len = mi->injections[pos].len; 1317 | if (new_code_len > 0) { 1318 | write_bytes(ci, (void*)mi->injections[pos].code, new_code_len); 1319 | } 1320 | 1321 | opcode = readU1(ci); 1322 | if (opcode == JVM_OPC_wide) { 1323 | ClassOpcode wopcode; 1324 | 1325 | writeU1(ci, opcode); 1326 | 1327 | wopcode = copyU1(ci); 1328 | /* lvIndex not used */ 1329 | (void)copyU2(ci); 1330 | verify_opc_wide(ci, wopcode); 1331 | if ( wopcode==JVM_OPC_iinc ) { 1332 | (void)copyU1(ci); 1333 | (void)copyU1(ci); 1334 | } 1335 | } else { 1336 | 1337 | ClassOpcode new_opcode; 1338 | int header; 1339 | int newHeader; 1340 | int low; 1341 | int high; 1342 | int i; 1343 | int npairs; 1344 | int widened; 1345 | int instr_len; 1346 | int delta; 1347 | int new_delta; 1348 | 1349 | switch (opcode) { 1350 | 1351 | case JVM_OPC_tableswitch: 1352 | header = NEXT_4BYTE_BOUNDARY(pos); 1353 | newHeader = NEXT_4BYTE_BOUNDARY(new_pos); 1354 | 1355 | skip(ci, header - (pos+1)); 1356 | 1357 | delta = readU4(ci); 1358 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1359 | low = readU4(ci); 1360 | high = readU4(ci); 1361 | 1362 | writeU1(ci, opcode); 1363 | for (i = new_pos+1; i < newHeader; ++i) { 1364 | writeU1(ci, 0); 1365 | } 1366 | writeU4(ci, new_delta); 1367 | writeU4(ci, low); 1368 | writeU4(ci, high); 1369 | 1370 | for (i = low; i <= high; ++i) { 1371 | delta = readU4(ci); 1372 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1373 | writeU4(ci, new_delta); 1374 | } 1375 | break; 1376 | 1377 | case JVM_OPC_lookupswitch: 1378 | header = NEXT_4BYTE_BOUNDARY(pos); 1379 | newHeader = NEXT_4BYTE_BOUNDARY(new_pos); 1380 | 1381 | skip(ci, header - (pos+1)); 1382 | 1383 | delta = readU4(ci); 1384 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1385 | npairs = readU4(ci); 1386 | writeU1(ci, opcode); 1387 | for (i = new_pos+1; i < newHeader; ++i) { 1388 | writeU1(ci, 0); 1389 | } 1390 | writeU4(ci, new_delta); 1391 | writeU4(ci, npairs); 1392 | for (i = 0; i< npairs; ++i) { 1393 | unsigned match = readU4(ci); 1394 | delta = readU4(ci); 1395 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1396 | writeU4(ci, match); 1397 | writeU4(ci, new_delta); 1398 | } 1399 | break; 1400 | 1401 | case JVM_OPC_jsr: case JVM_OPC_goto: 1402 | case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt: 1403 | case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne: 1404 | case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge: 1405 | case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt: 1406 | case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne: 1407 | case JVM_OPC_ifnull: case JVM_OPC_ifnonnull: 1408 | widened = mi->widening[pos]; 1409 | delta = readS2(ci); 1410 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1411 | new_opcode = opcode; 1412 | if (widened == 0) { 1413 | writeU1(ci, opcode); 1414 | writeU2(ci, new_delta); 1415 | } else if (widened == 2) { 1416 | switch (opcode) { 1417 | case JVM_OPC_jsr: 1418 | new_opcode = JVM_OPC_jsr_w; 1419 | break; 1420 | case JVM_OPC_goto: 1421 | new_opcode = JVM_OPC_goto_w; 1422 | break; 1423 | default: 1424 | CRW_FATAL(ci, "unexpected opcode"); 1425 | break; 1426 | } 1427 | writeU1(ci, new_opcode); 1428 | writeU4(ci, new_delta); 1429 | } else if (widened == 5) { 1430 | switch (opcode) { 1431 | case JVM_OPC_ifeq: 1432 | new_opcode = JVM_OPC_ifne; 1433 | break; 1434 | case JVM_OPC_ifge: 1435 | new_opcode = JVM_OPC_iflt; 1436 | break; 1437 | case JVM_OPC_ifgt: 1438 | new_opcode = JVM_OPC_ifle; 1439 | break; 1440 | case JVM_OPC_ifle: 1441 | new_opcode = JVM_OPC_ifgt; 1442 | break; 1443 | case JVM_OPC_iflt: 1444 | new_opcode = JVM_OPC_ifge; 1445 | break; 1446 | case JVM_OPC_ifne: 1447 | new_opcode = JVM_OPC_ifeq; 1448 | break; 1449 | case JVM_OPC_if_icmpeq: 1450 | new_opcode = JVM_OPC_if_icmpne; 1451 | break; 1452 | case JVM_OPC_if_icmpne: 1453 | new_opcode = JVM_OPC_if_icmpeq; 1454 | break; 1455 | case JVM_OPC_if_icmpge: 1456 | new_opcode = JVM_OPC_if_icmplt; 1457 | break; 1458 | case JVM_OPC_if_icmpgt: 1459 | new_opcode = JVM_OPC_if_icmple; 1460 | break; 1461 | case JVM_OPC_if_icmple: 1462 | new_opcode = JVM_OPC_if_icmpgt; 1463 | break; 1464 | case JVM_OPC_if_icmplt: 1465 | new_opcode = JVM_OPC_if_icmpge; 1466 | break; 1467 | case JVM_OPC_if_acmpeq: 1468 | new_opcode = JVM_OPC_if_acmpne; 1469 | break; 1470 | case JVM_OPC_if_acmpne: 1471 | new_opcode = JVM_OPC_if_acmpeq; 1472 | break; 1473 | case JVM_OPC_ifnull: 1474 | new_opcode = JVM_OPC_ifnonnull; 1475 | break; 1476 | case JVM_OPC_ifnonnull: 1477 | new_opcode = JVM_OPC_ifnull; 1478 | break; 1479 | default: 1480 | CRW_FATAL(ci, "Unexpected opcode"); 1481 | break; 1482 | } 1483 | writeU1(ci, new_opcode); /* write inverse branch */ 1484 | writeU2(ci, 3 + 5); /* beyond if and goto_w */ 1485 | writeU1(ci, JVM_OPC_goto_w); /* add a goto_w */ 1486 | writeU4(ci, new_delta-3); /* write new and wide delta */ 1487 | } else { 1488 | CRW_FATAL(ci, "Unexpected widening"); 1489 | } 1490 | break; 1491 | 1492 | case JVM_OPC_jsr_w: 1493 | case JVM_OPC_goto_w: 1494 | delta = readU4(ci); 1495 | new_delta = method_code_map(mi,pos+delta) - new_pos; 1496 | writeU1(ci, opcode); 1497 | writeU4(ci, new_delta); 1498 | break; 1499 | 1500 | default: 1501 | instr_len = opcode_length(ci, opcode); 1502 | writeU1(ci, opcode); 1503 | copy(ci, instr_len-1); 1504 | break; 1505 | } 1506 | } 1507 | } 1508 | 1509 | static void 1510 | method_inject_and_write_code(MethodImage *mi) 1511 | { 1512 | ByteCode bytecodes[LARGEST_INJECTION+1]; 1513 | ByteOffset len; 1514 | 1515 | CRW_ASSERT_MI(mi); 1516 | 1517 | /* Do injections */ 1518 | rewind_to_beginning_of_input_bytecodes(mi); 1519 | len = entry_injection_code(mi, bytecodes, (int)sizeof(bytecodes)); 1520 | if ( len > 0 ) { 1521 | int pos; 1522 | 1523 | pos = 0; 1524 | inject_bytecodes(mi, pos, bytecodes, len); 1525 | /* Adjust pos 0 to map to new pos 0, you never want to 1526 | * jump into this entry code injection. So the new pos 0 1527 | * will be past this entry_injection_code(). 1528 | */ 1529 | adjust_map(mi, pos, len); /* Inject before behavior */ 1530 | } 1531 | while (input_code_offset(mi) < mi->code_len) { 1532 | inject_for_opcode(mi); 1533 | } 1534 | 1535 | /* Adjust instructions */ 1536 | rewind_to_beginning_of_input_bytecodes(mi); 1537 | while (input_code_offset(mi) < mi->code_len) { 1538 | if (!adjust_instruction(mi)) { 1539 | rewind_to_beginning_of_input_bytecodes(mi); 1540 | } 1541 | } 1542 | 1543 | /* Write new instructions */ 1544 | rewind_to_beginning_of_input_bytecodes(mi); 1545 | while (input_code_offset(mi) < mi->code_len) { 1546 | write_instruction(mi); 1547 | } 1548 | } 1549 | 1550 | static void 1551 | copy_attribute(CrwClassImage *ci) 1552 | { 1553 | int len; 1554 | 1555 | (void)copyU2(ci); 1556 | len = copyU4(ci); 1557 | copy(ci, len); 1558 | } 1559 | 1560 | static void 1561 | copy_attributes(CrwClassImage *ci) 1562 | { 1563 | unsigned i; 1564 | unsigned count; 1565 | 1566 | count = copyU2(ci); 1567 | for (i = 0; i < count; ++i) { 1568 | copy_attribute(ci); 1569 | } 1570 | } 1571 | 1572 | static void 1573 | copy_all_fields(CrwClassImage *ci) 1574 | { 1575 | unsigned i; 1576 | unsigned count; 1577 | 1578 | count = copyU2(ci); 1579 | for (i = 0; i < count; ++i) { 1580 | /* access, name, descriptor */ 1581 | copy(ci, 6); 1582 | copy_attributes(ci); 1583 | } 1584 | } 1585 | 1586 | static void 1587 | write_line_table(MethodImage *mi) 1588 | { 1589 | unsigned i; 1590 | unsigned count; 1591 | CrwClassImage * ci; 1592 | 1593 | CRW_ASSERT_MI(mi); 1594 | ci = mi->ci; 1595 | (void)copyU4(ci); 1596 | count = copyU2(ci); 1597 | for(i=0; ici; 1624 | (void)copyU4(ci); 1625 | count = copyU2(ci); 1626 | for(i=0; icode_len > 65535 ) { 1661 | return readU4(mi->ci); 1662 | } 1663 | return readU2(mi->ci); 1664 | } 1665 | 1666 | static void 1667 | writeUoffset(MethodImage *mi, unsigned val) 1668 | { 1669 | if ( mi->new_code_len > 65535 ) { 1670 | writeU4(mi->ci, val); 1671 | } 1672 | writeU2(mi->ci, val); 1673 | } 1674 | 1675 | static unsigned 1676 | copyUoffset(MethodImage *mi) 1677 | { 1678 | unsigned uoffset; 1679 | 1680 | uoffset = readUoffset(mi); 1681 | writeUoffset(mi, uoffset); 1682 | return uoffset; 1683 | } 1684 | 1685 | /* Copy over verification_type_info structure */ 1686 | static void 1687 | copy_verification_types(MethodImage *mi, int ntypes) 1688 | { 1689 | /* If there were ntypes, we just copy that over, no changes */ 1690 | if ( ntypes > 0 ) { 1691 | int j; 1692 | 1693 | for ( j = 0 ; j < ntypes ; j++ ) { 1694 | unsigned tag; 1695 | 1696 | tag = copyU1(mi->ci); 1697 | switch ( tag ) { 1698 | case JVM_ITEM_Object: 1699 | (void)copyU2(mi->ci); /* Constant pool entry */ 1700 | break; 1701 | case JVM_ITEM_Uninitialized: 1702 | /* Code offset for 'new' opcode is for this object */ 1703 | writeUoffset(mi, method_code_map(mi, readUoffset(mi))); 1704 | break; 1705 | } 1706 | } 1707 | } 1708 | } 1709 | 1710 | /* Process the StackMapTable attribute. We didn't add any basic blocks 1711 | * so the frame count remains the same but we may need to process the 1712 | * frame types due to offset changes putting things out of range. 1713 | */ 1714 | static void 1715 | write_stackmap_table(MethodImage *mi) 1716 | { 1717 | CrwClassImage *ci; 1718 | CrwPosition save_position; 1719 | ByteOffset last_pc; 1720 | ByteOffset last_new_pc; 1721 | unsigned i; 1722 | unsigned attr_len; 1723 | unsigned new_attr_len; 1724 | unsigned count; 1725 | unsigned delta_adj; 1726 | 1727 | CRW_ASSERT_MI(mi); 1728 | ci = mi->ci; 1729 | 1730 | /* Save the position of the attribute length so we can fix it later */ 1731 | save_position = ci->output_position; 1732 | attr_len = copyU4(ci); 1733 | count = copyUoffset(mi); /* uoffset: number_of_entries */ 1734 | if ( count == 0 ) { 1735 | CRW_ASSERT(ci, attr_len==2); 1736 | return; 1737 | } 1738 | 1739 | /* Process entire stackmap */ 1740 | last_pc = 0; 1741 | last_new_pc = 0; 1742 | delta_adj = 0; 1743 | for ( i = 0 ; i < count ; i++ ) { 1744 | ByteOffset new_pc=0; /* new pc in instrumented code */ 1745 | unsigned ft; /* frame_type */ 1746 | int delta=0; /* pc delta */ 1747 | int new_delta=0; /* new pc delta */ 1748 | 1749 | ft = readU1(ci); 1750 | if ( ft <= 63 ) { 1751 | /* Frame Type: same_frame ([0,63]) */ 1752 | unsigned new_ft; /* new frame_type */ 1753 | 1754 | delta = (delta_adj + ft); 1755 | new_pc = method_code_map(mi, last_pc + delta); 1756 | new_delta = new_pc - last_new_pc; 1757 | new_ft = (new_delta - delta_adj); 1758 | if ( new_ft > 63 ) { 1759 | /* Change to same_frame_extended (251) */ 1760 | new_ft = 251; 1761 | writeU1(ci, new_ft); 1762 | writeUoffset(mi, (new_delta - delta_adj)); 1763 | } else { 1764 | writeU1(ci, new_ft); 1765 | } 1766 | } else if ( ft >= 64 && ft <= 127 ) { 1767 | /* Frame Type: same_locals_1_stack_item_frame ([64,127]) */ 1768 | unsigned new_ft; /* new frame_type */ 1769 | 1770 | delta = (delta_adj + ft - 64); 1771 | new_pc = method_code_map(mi, last_pc + delta); 1772 | new_delta = new_pc - last_new_pc; 1773 | if ( (new_delta - delta_adj) > 63 ) { 1774 | /* Change to same_locals_1_stack_item_frame_extended (247) */ 1775 | new_ft = 247; 1776 | writeU1(ci, new_ft); 1777 | writeUoffset(mi, (new_delta - delta_adj)); 1778 | } else { 1779 | new_ft = (new_delta - delta_adj) + 64; 1780 | writeU1(ci, new_ft); 1781 | } 1782 | copy_verification_types(mi, 1); 1783 | } else if ( ft >= 128 && ft <= 246 ) { 1784 | /* Frame Type: reserved_for_future_use ([128,246]) */ 1785 | CRW_FATAL(ci, "Unknown frame type in StackMapTable attribute"); 1786 | } else if ( ft == 247 ) { 1787 | /* Frame Type: same_locals_1_stack_item_frame_extended (247) */ 1788 | delta = (delta_adj + readUoffset(mi)); 1789 | new_pc = method_code_map(mi, last_pc + delta); 1790 | new_delta = new_pc - last_new_pc; 1791 | writeU1(ci, ft); 1792 | writeUoffset(mi, (new_delta - delta_adj)); 1793 | copy_verification_types(mi, 1); 1794 | } else if ( ft >= 248 && ft <= 250 ) { 1795 | /* Frame Type: chop_frame ([248,250]) */ 1796 | delta = (delta_adj + readUoffset(mi)); 1797 | new_pc = method_code_map(mi, last_pc + delta); 1798 | new_delta = new_pc - last_new_pc; 1799 | writeU1(ci, ft); 1800 | writeUoffset(mi, (new_delta - delta_adj)); 1801 | } else if ( ft == 251 ) { 1802 | /* Frame Type: same_frame_extended (251) */ 1803 | delta = (delta_adj + readUoffset(mi)); 1804 | new_pc = method_code_map(mi, last_pc + delta); 1805 | new_delta = new_pc - last_new_pc; 1806 | writeU1(ci, ft); 1807 | writeUoffset(mi, (new_delta - delta_adj)); 1808 | } else if ( ft >= 252 && ft <= 254 ) { 1809 | /* Frame Type: append_frame ([252,254]) */ 1810 | delta = (delta_adj + readUoffset(mi)); 1811 | new_pc = method_code_map(mi, last_pc + delta); 1812 | new_delta = new_pc - last_new_pc; 1813 | writeU1(ci, ft); 1814 | writeUoffset(mi, (new_delta - delta_adj)); 1815 | copy_verification_types(mi, (ft - 251)); 1816 | } else if ( ft == 255 ) { 1817 | unsigned ntypes; 1818 | 1819 | /* Frame Type: full_frame (255) */ 1820 | delta = (delta_adj + readUoffset(mi)); 1821 | new_pc = method_code_map(mi, last_pc + delta); 1822 | new_delta = new_pc - last_new_pc; 1823 | writeU1(ci, ft); 1824 | writeUoffset(mi, (new_delta - delta_adj)); 1825 | ntypes = copyU2(ci); /* ulocalvar */ 1826 | copy_verification_types(mi, ntypes); 1827 | ntypes = copyU2(ci); /* ustack */ 1828 | copy_verification_types(mi, ntypes); 1829 | } 1830 | 1831 | /* Update last_pc and last_new_pc (save on calls to method_code_map) */ 1832 | CRW_ASSERT(ci, delta >= 0); 1833 | CRW_ASSERT(ci, new_delta >= 0); 1834 | last_pc += delta; 1835 | last_new_pc = new_pc; 1836 | CRW_ASSERT(ci, last_pc <= mi->code_len); 1837 | CRW_ASSERT(ci, last_new_pc <= mi->new_code_len); 1838 | 1839 | /* Delta adjustment, all deltas are -1 now in attribute */ 1840 | delta_adj = 1; 1841 | } 1842 | 1843 | /* Update the attribute length */ 1844 | new_attr_len = ci->output_position - (save_position + 4); 1845 | CRW_ASSERT(ci, new_attr_len >= attr_len); 1846 | random_writeU4(ci, save_position, new_attr_len); 1847 | } 1848 | 1849 | /* Process the CLDC StackMap attribute. We didn't add any basic blocks 1850 | * so the frame count remains the same but we may need to process the 1851 | * frame types due to offset changes putting things out of range. 1852 | */ 1853 | static void 1854 | write_cldc_stackmap_table(MethodImage *mi) 1855 | { 1856 | CrwClassImage *ci; 1857 | CrwPosition save_position; 1858 | unsigned i; 1859 | unsigned attr_len; 1860 | unsigned new_attr_len; 1861 | unsigned count; 1862 | 1863 | CRW_ASSERT_MI(mi); 1864 | ci = mi->ci; 1865 | 1866 | /* Save the position of the attribute length so we can fix it later */ 1867 | save_position = ci->output_position; 1868 | attr_len = copyU4(ci); 1869 | count = copyUoffset(mi); /* uoffset: number_of_entries */ 1870 | if ( count == 0 ) { 1871 | CRW_ASSERT(ci, attr_len==2); 1872 | return; 1873 | } 1874 | 1875 | /* Process entire stackmap */ 1876 | for ( i = 0 ; i < count ; i++ ) { 1877 | unsigned ntypes; 1878 | 1879 | writeUoffset(mi, method_code_map(mi, readUoffset(mi))); 1880 | ntypes = copyU2(ci); /* ulocalvar */ 1881 | copy_verification_types(mi, ntypes); 1882 | ntypes = copyU2(ci); /* ustack */ 1883 | copy_verification_types(mi, ntypes); 1884 | } 1885 | 1886 | /* Update the attribute length */ 1887 | new_attr_len = ci->output_position - (save_position + 4); 1888 | CRW_ASSERT(ci, new_attr_len >= attr_len); 1889 | random_writeU4(ci, save_position, new_attr_len); 1890 | } 1891 | 1892 | static void 1893 | method_write_exception_table(MethodImage *mi) 1894 | { 1895 | unsigned i; 1896 | unsigned count; 1897 | CrwClassImage * ci; 1898 | 1899 | CRW_ASSERT_MI(mi); 1900 | ci = mi->ci; 1901 | count = copyU2(ci); 1902 | for(i=0; ici; 1949 | name_index = copyU2(ci); 1950 | if ( attribute_match(ci, name_index, "LineNumberTable") ) { 1951 | write_line_table(mi); 1952 | } else if ( attribute_match(ci, name_index, "LocalVariableTable") ) { 1953 | write_var_table(mi); 1954 | } else if ( attribute_match(ci, name_index, "LocalVariableTypeTable") ) { 1955 | write_var_table(mi); /* Exact same format as the LocalVariableTable */ 1956 | } else if ( attribute_match(ci, name_index, "StackMapTable") ) { 1957 | write_stackmap_table(mi); 1958 | } else if ( attribute_match(ci, name_index, "StackMap") ) { 1959 | write_cldc_stackmap_table(mi); 1960 | } else { 1961 | unsigned len; 1962 | len = copyU4(ci); 1963 | copy(ci, len); 1964 | } 1965 | } 1966 | 1967 | static int 1968 | is_init_method(const char *name) 1969 | { 1970 | if ( name!=NULL && strcmp(name,"")==0 ) { 1971 | return JNI_TRUE; 1972 | } 1973 | return JNI_FALSE; 1974 | } 1975 | 1976 | static int 1977 | is_clinit_method(const char *name) 1978 | { 1979 | if ( name!=NULL && strcmp(name,"")==0 ) { 1980 | return JNI_TRUE; 1981 | } 1982 | return JNI_FALSE; 1983 | } 1984 | 1985 | static int 1986 | is_finalize_method(const char *name) 1987 | { 1988 | if ( name!=NULL && strcmp(name,"finalize")==0 ) { 1989 | return JNI_TRUE; 1990 | } 1991 | return JNI_FALSE; 1992 | } 1993 | 1994 | static int 1995 | skip_method(CrwClassImage *ci, const char *name, 1996 | unsigned access_flags, ByteOffset code_len, 1997 | int system_class, jboolean *pskip_call_return_sites) 1998 | { 1999 | *pskip_call_return_sites = JNI_FALSE; 2000 | if ( system_class ) { 2001 | if ( code_len == 1 && is_init_method(name) ) { 2002 | return JNI_TRUE; 2003 | } else if ( code_len == 1 && is_finalize_method(name) ) { 2004 | return JNI_TRUE; 2005 | } else if ( is_clinit_method(name) ) { 2006 | return JNI_TRUE; 2007 | } else if ( ci->is_thread_class && strcmp(name,"currentThread")==0 ) { 2008 | return JNI_TRUE; 2009 | } 2010 | /* 2011 | if ( access_flags & JVM_ACC_PRIVATE ) { 2012 | *pskip_call_return_sites = JNI_TRUE; 2013 | } 2014 | */ 2015 | } 2016 | return JNI_FALSE; 2017 | } 2018 | 2019 | /* Process all code attributes */ 2020 | static void 2021 | method_write_bytecodes(CrwClassImage *ci, unsigned mnum, unsigned access_flags) 2022 | { 2023 | CrwPosition output_attr_len_position; 2024 | CrwPosition output_max_stack_position; 2025 | CrwPosition output_code_len_position; 2026 | CrwPosition start_of_output_bytecodes; 2027 | unsigned i; 2028 | unsigned attr_len; 2029 | unsigned max_stack; 2030 | ByteOffset code_len; 2031 | unsigned attr_count; 2032 | unsigned new_attr_len; 2033 | MethodImage * mi; 2034 | jboolean object_init_method; 2035 | jboolean skip_call_return_sites; 2036 | 2037 | CRW_ASSERT_CI(ci); 2038 | 2039 | /* Attribute Length */ 2040 | output_attr_len_position = ci->output_position; 2041 | attr_len = copyU4(ci); 2042 | 2043 | /* Max Stack */ 2044 | output_max_stack_position = ci->output_position; 2045 | max_stack = copyU2(ci); 2046 | 2047 | /* Max Locals */ 2048 | (void)copyU2(ci); 2049 | 2050 | /* Code Length */ 2051 | output_code_len_position = ci->output_position; 2052 | code_len = copyU4(ci); 2053 | start_of_output_bytecodes = ci->output_position; 2054 | 2055 | /* Some methods should not be instrumented */ 2056 | object_init_method = JNI_FALSE; 2057 | skip_call_return_sites = JNI_FALSE; 2058 | if ( ci->is_object_class && 2059 | is_init_method(ci->method_name[mnum]) && 2060 | strcmp(ci->method_descr[mnum],"()V")==0 ) { 2061 | object_init_method = JNI_TRUE; 2062 | skip_call_return_sites = JNI_TRUE; 2063 | } else if ( skip_method(ci, ci->method_name[mnum], access_flags, 2064 | code_len, ci->system_class, &skip_call_return_sites) ) { 2065 | /* Copy remainder minus already copied, the U2 max_stack, 2066 | * U2 max_locals, and U4 code_length fields have already 2067 | * been processed. 2068 | */ 2069 | copy(ci, attr_len - (2+2+4)); 2070 | return; 2071 | } 2072 | 2073 | /* Start Injection */ 2074 | mi = method_init(ci, mnum, code_len); 2075 | mi->object_init_method = object_init_method; 2076 | mi->access_flags = access_flags; 2077 | mi->skip_call_return_sites = skip_call_return_sites; 2078 | 2079 | /* Save the current position as the start of the input bytecodes */ 2080 | mi->start_of_input_bytecodes = ci->input_position; 2081 | 2082 | /* The max stack may increase */ 2083 | mi->max_stack = max_stack; 2084 | mi->new_max_stack = max_stack; 2085 | 2086 | /* Adjust all code offsets */ 2087 | method_inject_and_write_code(mi); 2088 | 2089 | /* Fix up code length (save new_code_len for later attribute processing) */ 2090 | mi->new_code_len = (int)(ci->output_position - start_of_output_bytecodes); 2091 | random_writeU4(ci, output_code_len_position, mi->new_code_len); 2092 | 2093 | /* Fixup max stack */ 2094 | CRW_ASSERT(ci, mi->new_max_stack <= 0xFFFF); 2095 | random_writeU2(ci, output_max_stack_position, mi->new_max_stack); 2096 | 2097 | /* Copy exception table */ 2098 | method_write_exception_table(mi); 2099 | 2100 | /* Copy code attributes (needs mi->new_code_len) */ 2101 | attr_count = copyU2(ci); 2102 | for (i = 0; i < attr_count; ++i) { 2103 | method_write_code_attribute(mi); 2104 | } 2105 | 2106 | /* Fix up attribute length */ 2107 | new_attr_len = (int)(ci->output_position - (output_attr_len_position + 4)); 2108 | random_writeU4(ci, output_attr_len_position, new_attr_len); 2109 | 2110 | /* Free method data */ 2111 | method_term(mi); 2112 | mi = NULL; 2113 | 2114 | } 2115 | 2116 | static void 2117 | method_write(CrwClassImage *ci, unsigned mnum) 2118 | { 2119 | unsigned i; 2120 | unsigned access_flags; 2121 | CrwCpoolIndex name_index; 2122 | CrwCpoolIndex descr_index; 2123 | unsigned attr_count; 2124 | 2125 | access_flags = copyU2(ci); 2126 | name_index = copyU2(ci); 2127 | ci->method_name[mnum] = cpool_entry(ci, name_index).ptr; 2128 | descr_index = copyU2(ci); 2129 | ci->method_descr[mnum] = cpool_entry(ci, descr_index).ptr; 2130 | attr_count = copyU2(ci); 2131 | 2132 | for (i = 0; i < attr_count; ++i) { 2133 | CrwCpoolIndex name_index; 2134 | 2135 | name_index = copyU2(ci); 2136 | if ( attribute_match(ci, name_index, "Code") ) { 2137 | method_write_bytecodes(ci, mnum, access_flags); 2138 | } else { 2139 | unsigned len; 2140 | len = copyU4(ci); 2141 | copy(ci, len); 2142 | } 2143 | } 2144 | } 2145 | 2146 | static void 2147 | method_write_all(CrwClassImage *ci) 2148 | { 2149 | unsigned i; 2150 | unsigned count; 2151 | 2152 | count = copyU2(ci); 2153 | ci->method_count = count; 2154 | if ( count > 0 ) { 2155 | ci->method_name = (const char **)allocate_clean(ci, count*(int)sizeof(const char*)); 2156 | ci->method_descr = (const char **)allocate_clean(ci, count*(int)sizeof(const char*)); 2157 | } 2158 | 2159 | for (i = 0; i < count; ++i) { 2160 | method_write(ci, i); 2161 | } 2162 | 2163 | if ( ci->mnum_callback != NULL ) { 2164 | (*(ci->mnum_callback))(ci->number, ci->method_name, ci->method_descr, 2165 | count); 2166 | } 2167 | } 2168 | 2169 | /* ------------------------------------------------------------------- */ 2170 | /* Cleanup function. */ 2171 | 2172 | static void 2173 | cleanup(CrwClassImage *ci) 2174 | { 2175 | CRW_ASSERT_CI(ci); 2176 | if ( ci->name != NULL ) { 2177 | deallocate(ci, (void*)ci->name); 2178 | ci->name = NULL; 2179 | } 2180 | if ( ci->method_name != NULL ) { 2181 | deallocate(ci, (void*)ci->method_name); 2182 | ci->method_name = NULL; 2183 | } 2184 | if ( ci->method_descr != NULL ) { 2185 | deallocate(ci, (void*)ci->method_descr); 2186 | ci->method_descr = NULL; 2187 | } 2188 | if ( ci->cpool != NULL ) { 2189 | CrwCpoolIndex i; 2190 | for(i=0; icpool_count_plus_one; i++) { 2191 | if ( ci->cpool[i].ptr != NULL ) { 2192 | deallocate(ci, (void*)(ci->cpool[i].ptr)); 2193 | ci->cpool[i].ptr = NULL; 2194 | } 2195 | } 2196 | deallocate(ci, (void*)ci->cpool); 2197 | ci->cpool = NULL; 2198 | } 2199 | } 2200 | 2201 | static jboolean 2202 | skip_class(unsigned access_flags) 2203 | { 2204 | if ( access_flags & JVM_ACC_INTERFACE ) { 2205 | return JNI_TRUE; 2206 | } 2207 | return JNI_FALSE; 2208 | } 2209 | 2210 | static long 2211 | inject_class(struct CrwClassImage *ci, 2212 | int system_class, 2213 | char* tclass_name, 2214 | char* tclass_sig, 2215 | char* call_name, 2216 | char* call_sig, 2217 | char* return_name, 2218 | char* return_sig, 2219 | char* obj_init_name, 2220 | char* obj_init_sig, 2221 | char* newarray_name, 2222 | char* newarray_sig, 2223 | unsigned char *buf, 2224 | long buf_len) 2225 | { 2226 | CrwConstantPoolEntry cs; 2227 | CrwCpoolIndex this_class; 2228 | CrwCpoolIndex super_class; 2229 | unsigned magic; 2230 | unsigned classfileMajorVersion; 2231 | unsigned classfileMinorVersion; 2232 | unsigned interface_count; 2233 | 2234 | CRW_ASSERT_CI(ci); 2235 | CRW_ASSERT(ci, buf!=NULL); 2236 | CRW_ASSERT(ci, buf_len!=0); 2237 | 2238 | CRW_ASSERT(ci, strchr(tclass_name,'.')==NULL); /* internal qualified name */ 2239 | 2240 | ci->injection_count = 0; 2241 | ci->system_class = system_class; 2242 | ci->tclass_name = tclass_name; 2243 | ci->tclass_sig = tclass_sig; 2244 | ci->call_name = call_name; 2245 | ci->call_sig = call_sig; 2246 | ci->return_name = return_name; 2247 | ci->return_sig = return_sig; 2248 | ci->obj_init_name = obj_init_name; 2249 | ci->obj_init_sig = obj_init_sig; 2250 | ci->newarray_name = newarray_name; 2251 | ci->newarray_sig = newarray_sig; 2252 | ci->output = buf; 2253 | ci->output_len = buf_len; 2254 | 2255 | magic = copyU4(ci); 2256 | CRW_ASSERT(ci, magic==0xCAFEBABE); 2257 | if ( magic != 0xCAFEBABE ) { 2258 | return (long)0; 2259 | } 2260 | 2261 | /* minor version number not used */ 2262 | classfileMinorVersion = copyU2(ci); 2263 | /* major version number not used */ 2264 | classfileMajorVersion = copyU2(ci); 2265 | /* CRW_ASSERT(ci, (classfileMajorVersion <= JVM_CLASSFILE_MAJOR_VERSION) || */ 2266 | /* ((classfileMajorVersion == JVM_CLASSFILE_MAJOR_VERSION) && */ 2267 | /* (classfileMinorVersion <= JVM_CLASSFILE_MINOR_VERSION))); */ 2268 | 2269 | cpool_setup(ci); 2270 | 2271 | ci->access_flags = copyU2(ci); 2272 | if ( skip_class(ci->access_flags) ) { 2273 | return (long)0; 2274 | } 2275 | 2276 | this_class = copyU2(ci); 2277 | 2278 | cs = cpool_entry(ci, (CrwCpoolIndex)(cpool_entry(ci, this_class).index1)); 2279 | if ( ci->name == NULL ) { 2280 | ci->name = duplicate(ci, cs.ptr, cs.len); 2281 | CRW_ASSERT(ci, strchr(ci->name,'.')==NULL); /* internal qualified name */ 2282 | } 2283 | CRW_ASSERT(ci, (int)strlen(ci->name)==cs.len && strncmp(ci->name, cs.ptr, cs.len)==0); 2284 | 2285 | super_class = copyU2(ci); 2286 | if ( super_class == 0 ) { 2287 | ci->is_object_class = JNI_TRUE; 2288 | CRW_ASSERT(ci, strcmp(ci->name,"java/lang/Object")==0); 2289 | } 2290 | 2291 | interface_count = copyU2(ci); 2292 | copy(ci, interface_count * 2); 2293 | 2294 | copy_all_fields(ci); 2295 | 2296 | method_write_all(ci); 2297 | 2298 | if ( ci->injection_count == 0 ) { 2299 | return (long)0; 2300 | } 2301 | 2302 | copy_attributes(ci); 2303 | 2304 | return (long)ci->output_position; 2305 | } 2306 | 2307 | /* ------------------------------------------------------------------- */ 2308 | /* Exported interfaces */ 2309 | 2310 | JNIEXPORT void JNICALL 2311 | java_crw_demo(unsigned class_number, 2312 | const char *name, 2313 | const unsigned char *file_image, 2314 | long file_len, 2315 | int system_class, 2316 | char* tclass_name, /* Name of class that has tracker methods. */ 2317 | char* tclass_sig, /* Signature of tclass */ 2318 | char* call_name, /* Method name to call at offset 0 */ 2319 | char* call_sig, /* Signature of this method */ 2320 | char* return_name, /* Method name to call before any return */ 2321 | char* return_sig, /* Signature of this method */ 2322 | char* obj_init_name, /* Method name to call in Object */ 2323 | char* obj_init_sig, /* Signature of this method */ 2324 | char* newarray_name, /* Method name to call after newarray opcodes */ 2325 | char* newarray_sig, /* Signature of this method */ 2326 | unsigned char **pnew_file_image, 2327 | long *pnew_file_len, 2328 | FatalErrorHandler fatal_error_handler, 2329 | MethodNumberRegister mnum_callback) 2330 | { 2331 | CrwClassImage ci; 2332 | long max_length; 2333 | long new_length; 2334 | void *new_image; 2335 | int len; 2336 | 2337 | /* Initial setup of the CrwClassImage structure */ 2338 | (void)memset(&ci, 0, (int)sizeof(CrwClassImage)); 2339 | ci.fatal_error_handler = fatal_error_handler; 2340 | ci.mnum_callback = mnum_callback; 2341 | 2342 | /* Do some interface error checks */ 2343 | if ( pnew_file_image==NULL ) { 2344 | CRW_FATAL(&ci, "pnew_file_image==NULL"); 2345 | } 2346 | if ( pnew_file_len==NULL ) { 2347 | CRW_FATAL(&ci, "pnew_file_len==NULL"); 2348 | } 2349 | 2350 | /* No file length means do nothing */ 2351 | *pnew_file_image = NULL; 2352 | *pnew_file_len = 0; 2353 | if ( file_len==0 ) { 2354 | return; 2355 | } 2356 | 2357 | /* Do some more interface error checks */ 2358 | if ( file_image == NULL ) { 2359 | CRW_FATAL(&ci, "file_image == NULL"); 2360 | } 2361 | if ( file_len < 0 ) { 2362 | CRW_FATAL(&ci, "file_len < 0"); 2363 | } 2364 | if ( system_class != 0 && system_class != 1 ) { 2365 | CRW_FATAL(&ci, "system_class is not 0 or 1"); 2366 | } 2367 | if ( tclass_name == NULL ) { 2368 | CRW_FATAL(&ci, "tclass_name == NULL"); 2369 | } 2370 | if ( tclass_sig == NULL || tclass_sig[0]!='L' ) { 2371 | CRW_FATAL(&ci, "tclass_sig is not a valid class signature"); 2372 | } 2373 | len = (int)strlen(tclass_sig); 2374 | if ( tclass_sig[len-1]!=';' ) { 2375 | CRW_FATAL(&ci, "tclass_sig is not a valid class signature"); 2376 | } 2377 | if ( call_name != NULL ) { 2378 | if ( call_sig == NULL || strcmp(call_sig, "(II)V") != 0 ) { 2379 | CRW_FATAL(&ci, "call_sig is not (II)V"); 2380 | } 2381 | } 2382 | if ( return_name != NULL ) { 2383 | if ( return_sig == NULL || strcmp(return_sig, "(II)V") != 0 ) { 2384 | CRW_FATAL(&ci, "return_sig is not (II)V"); 2385 | } 2386 | } 2387 | if ( obj_init_name != NULL ) { 2388 | if ( obj_init_sig == NULL || strcmp(obj_init_sig, "(Ljava/lang/Object;)V") != 0 ) { 2389 | CRW_FATAL(&ci, "obj_init_sig is not (Ljava/lang/Object;)V"); 2390 | } 2391 | } 2392 | if ( newarray_name != NULL ) { 2393 | if ( newarray_sig == NULL || strcmp(newarray_sig, "(Ljava/lang/Object;)V") != 0 ) { 2394 | CRW_FATAL(&ci, "newarray_sig is not (Ljava/lang/Object;)V"); 2395 | } 2396 | } 2397 | 2398 | /* Finish setup the CrwClassImage structure */ 2399 | ci.is_thread_class = JNI_FALSE; 2400 | if ( name != NULL ) { 2401 | CRW_ASSERT(&ci, strchr(name,'.')==NULL); /* internal qualified name */ 2402 | 2403 | ci.name = duplicate(&ci, name, (int)strlen(name)); 2404 | if ( strcmp(name, "java/lang/Thread")==0 ) { 2405 | ci.is_thread_class = JNI_TRUE; 2406 | } 2407 | } 2408 | ci.number = class_number; 2409 | ci.input = file_image; 2410 | ci.input_len = file_len; 2411 | 2412 | /* Do the injection */ 2413 | max_length = file_len*2 + 512; /* Twice as big + 512 */ 2414 | new_image = allocate(&ci, (int)max_length); 2415 | new_length = inject_class(&ci, 2416 | system_class, 2417 | tclass_name, 2418 | tclass_sig, 2419 | call_name, 2420 | call_sig, 2421 | return_name, 2422 | return_sig, 2423 | obj_init_name, 2424 | obj_init_sig, 2425 | newarray_name, 2426 | newarray_sig, 2427 | new_image, 2428 | max_length); 2429 | 2430 | /* Dispose or shrink the space to be returned. */ 2431 | if ( new_length == 0 ) { 2432 | deallocate(&ci, (void*)new_image); 2433 | new_image = NULL; 2434 | } else { 2435 | new_image = (void*)reallocate(&ci, (void*)new_image, (int)new_length); 2436 | } 2437 | 2438 | /* Return the new class image */ 2439 | *pnew_file_image = (unsigned char *)new_image; 2440 | *pnew_file_len = (long)new_length; 2441 | 2442 | /* Cleanup before we leave. */ 2443 | cleanup(&ci); 2444 | } 2445 | 2446 | /* Return the classname for this class which is inside the classfile image. */ 2447 | JNIEXPORT char * JNICALL 2448 | java_crw_demo_classname(const unsigned char *file_image, long file_len, 2449 | FatalErrorHandler fatal_error_handler) 2450 | { 2451 | CrwClassImage ci; 2452 | CrwConstantPoolEntry cs; 2453 | CrwCpoolIndex this_class; 2454 | unsigned magic; 2455 | char * name; 2456 | 2457 | name = NULL; 2458 | 2459 | if ( file_len==0 || file_image==NULL ) { 2460 | return name; 2461 | } 2462 | 2463 | /* The only fields we need filled in are the image pointer and the error 2464 | * handler. 2465 | * By not adding an output buffer pointer, no output is created. 2466 | */ 2467 | (void)memset(&ci, 0, (int)sizeof(CrwClassImage)); 2468 | ci.input = file_image; 2469 | ci.input_len = file_len; 2470 | ci.fatal_error_handler = fatal_error_handler; 2471 | 2472 | /* Read out the bytes from the classfile image */ 2473 | 2474 | magic = readU4(&ci); /* magic number */ 2475 | CRW_ASSERT(&ci, magic==0xCAFEBABE); 2476 | if ( magic != 0xCAFEBABE ) { 2477 | return name; 2478 | } 2479 | (void)readU2(&ci); /* minor version number */ 2480 | (void)readU2(&ci); /* major version number */ 2481 | 2482 | /* Read in constant pool. Since no output setup, writes are NOP's */ 2483 | cpool_setup(&ci); 2484 | 2485 | (void)readU2(&ci); /* access flags */ 2486 | this_class = readU2(&ci); /* 'this' class */ 2487 | 2488 | /* Get 'this' constant pool entry */ 2489 | cs = cpool_entry(&ci, (CrwCpoolIndex)(cpool_entry(&ci, this_class).index1)); 2490 | 2491 | /* Duplicate the name */ 2492 | name = (char *)duplicate(&ci, cs.ptr, cs.len); 2493 | 2494 | /* Cleanup before we leave. */ 2495 | cleanup(&ci); 2496 | 2497 | /* Return malloc space */ 2498 | return name; 2499 | } 2500 | -------------------------------------------------------------------------------- /java_crw_demo/java_crw_demo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions 6 | * are met: 7 | * 8 | * - Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 11 | * - Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * - Neither the name of Oracle nor the names of its 16 | * contributors may be used to endorse or promote products derived 17 | * from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef JAVA_CRW_DEMO_H 33 | #define JAVA_CRW_DEMO_H 34 | 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | /* This callback is used to notify the caller of a fatal error. */ 42 | 43 | typedef void (*FatalErrorHandler)(const char*message, const char*file, int line); 44 | 45 | /* This callback is used to return the method information for a class. 46 | * Since the information was already read here, it was useful to 47 | * return it here, with no JVMTI phase restrictions. 48 | * If the class file does represent a "class" and it has methods, then 49 | * this callback will be called with the class number and pointers to 50 | * the array of names, array of signatures, and the count of methods. 51 | */ 52 | 53 | typedef void (*MethodNumberRegister)(unsigned, const char**, const char**, int); 54 | 55 | /* Class file reader/writer interface. Basic input is a classfile image 56 | * and details about what to inject. The output is a new classfile image 57 | * that was allocated with malloc(), and should be freed by the caller. 58 | */ 59 | 60 | /* Names of external symbols to look for. These are the names that we 61 | * try and lookup in the shared library. On Windows 2000, the naming 62 | * convention is to prefix a "_" and suffix a "@N" where N is 4 times 63 | * the number or arguments supplied.It has 19 args, so 76 = 19*4. 64 | * On Windows 2003, Linux, and Solaris, the first name will be 65 | * found, on Windows 2000 a second try should find the second name. 66 | * 67 | * WARNING: If You change the JavaCrwDemo typedef, you MUST change 68 | * multiple things in this file, including this name. 69 | */ 70 | 71 | #define JAVA_CRW_DEMO_SYMBOLS { "java_crw_demo", "_java_crw_demo@76" } 72 | 73 | /* Typedef needed for type casting in dynamic access situations. */ 74 | 75 | typedef void (JNICALL *JavaCrwDemo)( 76 | unsigned class_number, 77 | const char *name, 78 | const unsigned char *file_image, 79 | long file_len, 80 | int system_class, 81 | char* tclass_name, 82 | char* tclass_sig, 83 | char* call_name, 84 | char* call_sig, 85 | char* return_name, 86 | char* return_sig, 87 | char* obj_init_name, 88 | char* obj_init_sig, 89 | char* newarray_name, 90 | char* newarray_sig, 91 | unsigned char **pnew_file_image, 92 | long *pnew_file_len, 93 | FatalErrorHandler fatal_error_handler, 94 | MethodNumberRegister mnum_callback 95 | ); 96 | 97 | /* Function export (should match typedef above) */ 98 | 99 | JNIEXPORT void JNICALL java_crw_demo( 100 | 101 | unsigned class_number, /* Caller assigned class number for class */ 102 | 103 | const char *name, /* Internal class name, e.g. java/lang/Object */ 104 | /* (Do not use "java.lang.Object" format) */ 105 | 106 | const unsigned char 107 | *file_image, /* Pointer to classfile image for this class */ 108 | 109 | long file_len, /* Length of the classfile in bytes */ 110 | 111 | int system_class, /* Set to 1 if this is a system class */ 112 | /* (prevents injections into empty */ 113 | /* , finalize, and methods) */ 114 | 115 | char* tclass_name, /* Class that has methods we will call at */ 116 | /* the injection sites (tclass) */ 117 | 118 | char* tclass_sig, /* Signature of tclass */ 119 | /* (Must be "L" + tclass_name + ";") */ 120 | 121 | char* call_name, /* Method name in tclass to call at offset 0 */ 122 | /* for every method */ 123 | 124 | char* call_sig, /* Signature of this call_name method */ 125 | /* (Must be "(II)V") */ 126 | 127 | char* return_name, /* Method name in tclass to call at all */ 128 | /* return opcodes in every method */ 129 | 130 | char* return_sig, /* Signature of this return_name method */ 131 | /* (Must be "(II)V") */ 132 | 133 | char* obj_init_name, /* Method name in tclass to call first thing */ 134 | /* when injecting java.lang.Object. */ 135 | 136 | char* obj_init_sig, /* Signature of this obj_init_name method */ 137 | /* (Must be "(Ljava/lang/Object;)V") */ 138 | 139 | char* newarray_name, /* Method name in tclass to call after every */ 140 | /* newarray opcode in every method */ 141 | 142 | char* newarray_sig, /* Signature of this method */ 143 | /* (Must be "(Ljava/lang/Object;II)V") */ 144 | 145 | unsigned char 146 | **pnew_file_image, /* Returns a pointer to new classfile image */ 147 | 148 | long *pnew_file_len, /* Returns the length of the new image */ 149 | 150 | FatalErrorHandler 151 | fatal_error_handler, /* Pointer to function to call on any */ 152 | /* fatal error. NULL sends error to stderr */ 153 | 154 | MethodNumberRegister 155 | mnum_callback /* Pointer to function that gets called */ 156 | /* with all details on methods in this */ 157 | /* class. NULL means skip this call. */ 158 | 159 | ); 160 | 161 | 162 | /* External to read the class name out of a class file . 163 | * 164 | * WARNING: If You change the typedef, you MUST change 165 | * multiple things in this file, including this name. 166 | */ 167 | 168 | #define JAVA_CRW_DEMO_CLASSNAME_SYMBOLS \ 169 | { "java_crw_demo_classname", "_java_crw_demo_classname@12" } 170 | 171 | /* Typedef needed for type casting in dynamic access situations. */ 172 | 173 | typedef char * (JNICALL *JavaCrwDemoClassname)( 174 | const unsigned char *file_image, 175 | long file_len, 176 | FatalErrorHandler fatal_error_handler); 177 | 178 | JNIEXPORT char * JNICALL java_crw_demo_classname( 179 | const unsigned char *file_image, 180 | long file_len, 181 | FatalErrorHandler fatal_error_handler); 182 | 183 | #ifdef __cplusplus 184 | } /* extern "C" */ 185 | #endif /* __cplusplus */ 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /java_crw_demo/sample.makefile.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # 8 | # - Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # - Redistributions in binary form must reproduce the above copyright 12 | # notice, this list of conditions and the following disclaimer in the 13 | # documentation and/or other materials provided with the distribution. 14 | # 15 | # - Neither the name of Oracle nor the names of its 16 | # contributors may be used to endorse or promote products derived 17 | # from this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | 32 | ######################################################################## 33 | # 34 | # Sample GNU Makefile for building 35 | # 36 | # Example uses: 37 | # gnumake JDK= OSNAME=solaris [OPT=true] [LIBARCH=sparc] 38 | # gnumake JDK= OSNAME=solaris [OPT=true] [LIBARCH=sparcv9] 39 | # gnumake JDK= OSNAME=linux [OPT=true] 40 | # gnumake JDK= OSNAME=win32 [OPT=true] 41 | # 42 | ######################################################################## 43 | 44 | # Source lists 45 | LIBNAME=java_crw_demo 46 | SOURCES=java_crw_demo.c 47 | 48 | # Solaris Sun C Compiler Version 5.5 49 | ifeq ($(OSNAME), solaris) 50 | # Sun Solaris Compiler options needed 51 | COMMON_FLAGS=-mt -KPIC 52 | # Options that help find errors 53 | COMMON_FLAGS+= -Xa -v -xstrconst -xc99=%none 54 | # Check LIBARCH for any special compiler options 55 | LIBARCH=$(shell uname -p) 56 | ifeq ($(LIBARCH), sparc) 57 | COMMON_FLAGS+=-xarch=v8 -xregs=no%appl 58 | endif 59 | ifeq ($(LIBARCH), sparcv9) 60 | COMMON_FLAGS+=-xarch=v9 -xregs=no%appl 61 | endif 62 | ifeq ($(OPT), true) 63 | CFLAGS=-xO2 $(COMMON_FLAGS) 64 | else 65 | CFLAGS=-g $(COMMON_FLAGS) 66 | endif 67 | # Object files needed to create library 68 | OBJECTS=$(SOURCES:%.c=%.o) 69 | # Library name and options needed to build it 70 | LIBRARY=lib$(LIBNAME).so 71 | LDFLAGS=-z defs -ztext 72 | # Libraries we are dependent on 73 | LIBRARIES=-lc 74 | # Building a shared library 75 | LINK_SHARED=$(LINK.c) -G -o $@ 76 | endif 77 | 78 | # Linux GNU C Compiler 79 | ifeq ($(OSNAME), linux) 80 | # GNU Compiler options needed to build it 81 | COMMON_FLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer 82 | # Options that help find errors 83 | COMMON_FLAGS+= -W -Wall -Wno-unused -Wno-parentheses 84 | ifeq ($(OPT), true) 85 | CFLAGS=-O2 $(COMMON_FLAGS) 86 | else 87 | CFLAGS=-g $(COMMON_FLAGS) 88 | endif 89 | # Object files needed to create library 90 | OBJECTS=$(SOURCES:%.c=%.o) 91 | # Library name and options needed to build it 92 | LIBRARY=lib$(LIBNAME).so 93 | LDFLAGS=-Wl,-soname=$(LIBRARY) -static-libgcc -mimpure-text 94 | # Libraries we are dependent on 95 | LIBRARIES=-lc 96 | # Building a shared library 97 | LINK_SHARED=$(LINK.c) -shared -o $@ 98 | endif 99 | 100 | # Linux GNU C Compiler 101 | ifeq ($(OSNAME), osx) 102 | # GNU Compiler options needed to build it 103 | COMMON_FLAGS=-fno-strict-aliasing -fPIC -fno-omit-frame-pointer 104 | # Options that help find errors 105 | COMMON_FLAGS+= -W -Wall -Wno-unused -Wno-parentheses 106 | ifeq ($(OPT), true) 107 | CFLAGS=-O2 $(COMMON_FLAGS) 108 | else 109 | CFLAGS=-g $(COMMON_FLAGS) 110 | endif 111 | # Object files needed to create library 112 | OBJECTS=$(SOURCES:%.c=%.o) 113 | # Library name and options needed to build it 114 | LIBRARY=lib$(LIBNAME).so 115 | LDFLAGS=-Wl -static-libgcc -mimpure-text 116 | # Libraries we are dependent on 117 | LIBRARIES=-lc 118 | # Building a shared library 119 | LINK_SHARED=$(LINK.c) -shared -o $@ 120 | endif 121 | 122 | # Windows Microsoft C/C++ Optimizing Compiler Version 12 123 | ifeq ($(OSNAME), win32) 124 | CC=cl 125 | # Compiler options needed to build it 126 | COMMON_FLAGS=-Gy -DWIN32 127 | # Options that help find errors 128 | COMMON_FLAGS+=-W0 -WX 129 | ifeq ($(OPT), true) 130 | CFLAGS= -Ox -Op -Zi $(COMMON_FLAGS) 131 | else 132 | CFLAGS= -Od -Zi $(COMMON_FLAGS) 133 | endif 134 | # Object files needed to create library 135 | OBJECTS=$(SOURCES:%.c=%.obj) 136 | # Library name and options needed to build it 137 | LIBRARY=$(LIBNAME).dll 138 | LDFLAGS= 139 | # Libraries we are dependent on 140 | LIBRARIES= 141 | # Building a shared library 142 | LINK_SHARED=link -dll -out:$@ 143 | endif 144 | 145 | # Common -I options 146 | CFLAGS += -I. 147 | CFLAGS += -I$(JDK)/include -I$(JDK)/include/$(OSNAME) 148 | 149 | # Default rule (build both native library and jar file) 150 | all: $(LIBRARY) 151 | 152 | # Build native library 153 | $(LIBRARY): $(OBJECTS) 154 | $(LINK_SHARED) $(OBJECTS) $(LIBRARIES) 155 | 156 | # Cleanup the built bits 157 | clean: 158 | rm -f -r classes 159 | rm -f $(LIBRARY) $(OBJECTS) 160 | 161 | # Compilation rule only needed on Windows 162 | ifeq ($(OSNAME), win32) 163 | %.obj: %.c 164 | $(COMPILE.c) $< 165 | endif 166 | 167 | -------------------------------------------------------------------------------- /u.c: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Twitter, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "u.h" 16 | #include 17 | #include 18 | 19 | void 20 | panic(const char *fmt, ...) 21 | { 22 | va_list ap; 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | fprintf(stderr, "\n"); 26 | fflush(stderr); 27 | va_end(ap); 28 | exit(1); 29 | } 30 | 31 | void* 32 | emalloc(uint n) 33 | { 34 | void *v; 35 | 36 | v = calloc(n, 1); 37 | if(v == nil) 38 | panic("out of memory"); 39 | return v; 40 | } 41 | 42 | void* 43 | erealloc(void *v, uint n) 44 | { 45 | v = realloc(v, n); 46 | if(v == nil) 47 | panic("out of memory"); 48 | return v; 49 | } 50 | 51 | char* 52 | estrdup(char *s) 53 | { 54 | s = strdup(s); 55 | if(s == nil) 56 | panic("out of memory"); 57 | return s; 58 | } 59 | -------------------------------------------------------------------------------- /u.h: -------------------------------------------------------------------------------- 1 | // Plan 9 from User Space include/u.h 2 | // http://code.swtch.com/plan9port/src/tip/include/u.h 3 | // 4 | // Copyright 2001-2007 Russ Cox. All Rights Reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | #ifndef _U_H_ 25 | #define _U_H_ 1 26 | #if defined(__cplusplus) 27 | extern "C" { 28 | #endif 29 | 30 | #define __BSD_VISIBLE 1 /* FreeBSD 5.x */ 31 | #if defined(__sun__) 32 | # define __EXTENSIONS__ 1 /* SunOS */ 33 | # if defined(__SunOS5_6__) || defined(__SunOS5_7__) || defined(__SunOS5_8__) || defined(__SunOS5_9__) || defined(__SunOS5_10__) 34 | /* NOT USING #define __MAKECONTEXT_V2_SOURCE 1 / * SunOS */ 35 | # else 36 | /* What's left? */ 37 | # define __MAKECONTEXT_V2_SOURCE 1 38 | # endif 39 | #endif 40 | #define _BSD_SOURCE 1 41 | #define _NETBSD_SOURCE 1 /* NetBSD */ 42 | #define _SVID_SOURCE 1 43 | #if !defined(__APPLE__) && !defined(__OpenBSD__) 44 | # define _XOPEN_SOURCE 1000 45 | # define _XOPEN_SOURCE_EXTENDED 1 46 | #endif 47 | #if defined(__FreeBSD__) 48 | # include 49 | /* for strtoll */ 50 | # undef __ISO_C_VISIBLE 51 | # define __ISO_C_VISIBLE 1999 52 | # undef __LONG_LONG_SUPPORTED 53 | # define __LONG_LONG_SUPPORTED 54 | #endif 55 | #if defined(__AIX__) 56 | # define _XOPEN_SOURCE 1 57 | #endif 58 | #if defined(__APPLE__) 59 | # define _DARWIN_NO_64_BIT_INODE /* Snow Leopard */ 60 | #endif 61 | #define _LARGEFILE64_SOURCE 1 62 | #define _FILE_OFFSET_BITS 64 63 | 64 | #include 65 | 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include /* for tolower */ 76 | 77 | /* 78 | * OS-specific crap 79 | */ 80 | #define _NEEDUCHAR 1 81 | #define _NEEDUSHORT 1 82 | #define _NEEDUINT 1 83 | #define _NEEDULONG 1 84 | 85 | #if defined(__linux__) 86 | # include 87 | # if defined(__Linux26__) 88 | # include 89 | # define PLAN9PORT_USING_PTHREADS 1 90 | # endif 91 | # if defined(__USE_MISC) 92 | # undef _NEEDUSHORT 93 | # undef _NEEDUINT 94 | # undef _NEEDULONG 95 | # endif 96 | #elif defined(__sun__) 97 | # include 98 | # include 99 | # define PLAN9PORT_USING_PTHREADS 1 100 | # undef _NEEDUSHORT 101 | # undef _NEEDUINT 102 | # undef _NEEDULONG 103 | # define nil 0 /* no cast to void* */ 104 | #elif defined(__FreeBSD__) 105 | # include 106 | # include 107 | # if __FreeBSD_version >= 500000 108 | # define PLAN9PORT_USING_PTHREADS 1 109 | # include 110 | # endif 111 | # if !defined(_POSIX_SOURCE) 112 | # undef _NEEDUSHORT 113 | # undef _NEEDUINT 114 | # endif 115 | #elif defined(__APPLE__) 116 | # include 117 | # include 118 | # define PLAN9PORT_USING_PTHREADS 1 119 | # if __GNUC__ < 4 120 | # undef _NEEDUSHORT 121 | # undef _NEEDUINT 122 | # endif 123 | # undef _ANSI_SOURCE 124 | # undef _POSIX_C_SOURCE 125 | # undef _XOPEN_SOURCE 126 | # if !defined(NSIG) 127 | # define NSIG 32 128 | # endif 129 | # define _NEEDLL 1 130 | #elif defined(__NetBSD__) 131 | # include 132 | # include 133 | # undef _NEEDUSHORT 134 | # undef _NEEDUINT 135 | # undef _NEEDULONG 136 | #elif defined(__OpenBSD__) 137 | # include 138 | # undef _NEEDUSHORT 139 | # undef _NEEDUINT 140 | # undef _NEEDULONG 141 | #else 142 | /* No idea what system this is -- try some defaults */ 143 | # include 144 | #endif 145 | 146 | #ifndef O_DIRECT 147 | #define O_DIRECT 0 148 | #endif 149 | 150 | typedef signed char schar; 151 | 152 | #ifdef _NEEDUCHAR 153 | typedef unsigned char uchar; 154 | #endif 155 | #ifdef _NEEDUSHORT 156 | typedef unsigned short ushort; 157 | #endif 158 | #ifdef _NEEDUINT 159 | typedef unsigned int uint; 160 | #endif 161 | #ifdef _NEEDULONG 162 | typedef unsigned long ulong; 163 | #endif 164 | typedef unsigned long long uvlong; 165 | typedef long long vlong; 166 | 167 | typedef uint64_t u64int; 168 | typedef int64_t s64int; 169 | typedef uint8_t u8int; 170 | typedef int8_t s8int; 171 | typedef uint16_t u16int; 172 | typedef int16_t s16int; 173 | typedef uintptr_t uintptr; 174 | typedef intptr_t intptr; 175 | typedef uint32_t u32int; 176 | typedef int32_t s32int; 177 | 178 | typedef u32int uint32; 179 | typedef s32int int32; 180 | typedef u16int uint16; 181 | typedef s16int int16; 182 | typedef u64int uint64; 183 | typedef s64int int64; 184 | typedef u8int uint8; 185 | typedef s8int int8; 186 | 187 | #undef _NEEDUCHAR 188 | #undef _NEEDUSHORT 189 | #undef _NEEDUINT 190 | #undef _NEEDULONG 191 | 192 | #ifndef nil 193 | #define nil ((void*)0) 194 | #endif 195 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 196 | 197 | #ifndef offsetof 198 | #define offsetof(s, m) (ulong)(&(((s*)0)->m)) 199 | #endif 200 | 201 | void panic(const char *fmt, ...); 202 | 203 | void *erealloc(void*, uint); 204 | void *emalloc(uint); 205 | char *estrdup(char*); 206 | 207 | #if defined(__cplusplus) 208 | } 209 | #endif 210 | #endif --------------------------------------------------------------------------------