summaryrefslogtreecommitdiffstats
path: root/java-allocation-instrumenter
diff options
context:
space:
mode:
authorYohann Roussel <yroussel@google.com>2014-03-19 16:25:37 +0100
committerYohann Roussel <yroussel@google.com>2014-03-20 15:13:33 +0100
commit4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch)
treeee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /java-allocation-instrumenter
parent3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff)
downloadtoolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'java-allocation-instrumenter')
-rw-r--r--java-allocation-instrumenter/AUTHORS1
-rw-r--r--java-allocation-instrumenter/Android.mk48
-rw-r--r--java-allocation-instrumenter/COPYING202
-rw-r--r--java-allocation-instrumenter/NOTICE202
-rw-r--r--java-allocation-instrumenter/README.android4
-rw-r--r--java-allocation-instrumenter/build.xml65
-rw-r--r--java-allocation-instrumenter/etc/manifest.txt5
-rw-r--r--java-allocation-instrumenter/jarjar-rules.txt2
-rw-r--r--java-allocation-instrumenter/lib/asm-4.0.jarbin0 -> 46018 bytes
-rw-r--r--java-allocation-instrumenter/lib/asm-analysis-4.0.jarbin0 -> 19611 bytes
-rw-r--r--java-allocation-instrumenter/lib/asm-commons-4.0.jarbin0 -> 37774 bytes
-rw-r--r--java-allocation-instrumenter/lib/asm-tree-4.0.jarbin0 -> 21980 bytes
-rw-r--r--java-allocation-instrumenter/lib/asm-util-4.0.jarbin0 -> 36993 bytes
-rw-r--r--java-allocation-instrumenter/lib/asm-xml-4.0.jarbin0 -> 51707 bytes
-rw-r--r--java-allocation-instrumenter/lib/guava-r06.jarbin0 -> 934385 bytes
-rw-r--r--java-allocation-instrumenter/lib/jarjar-1.0.jarbin0 -> 112749 bytes
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java56
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java208
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java589
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java260
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java47
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java231
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java46
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java273
-rw-r--r--java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java129
25 files changed, 2368 insertions, 0 deletions
diff --git a/java-allocation-instrumenter/AUTHORS b/java-allocation-instrumenter/AUTHORS
new file mode 100644
index 0000000..e491a9e
--- /dev/null
+++ b/java-allocation-instrumenter/AUTHORS
@@ -0,0 +1 @@
+Google Inc.
diff --git a/java-allocation-instrumenter/Android.mk b/java-allocation-instrumenter/Android.mk
new file mode 100644
index 0000000..51654a9
--- /dev/null
+++ b/java-allocation-instrumenter/Android.mk
@@ -0,0 +1,48 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# java-allocation-instrumenter
+#
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
+
+LOCAL_JAR_MANIFEST := etc/manifest.txt
+
+LOCAL_MODULE := allocation-jack
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
+
+LOCAL_JAVA_LIBRARIES := \
+ guava-jack
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ asm-all-4.1-jack \
+ guava-collect-jack
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+$(LOCAL_BUILT_MODULE): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
+$(LOCAL_BUILT_MODULE): $(full_classes_compiled_jar) | $(JARJAR)
+ @echo JarJar: $@
+ $(hide) java -jar $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@
+ $(hide)jar umf $(PRIVATE_LOCAL_PATH)/etc/manifest.txt $@
+
+$(call dist-for-goals, dist_files, $(LOCAL_BUILT_MODULE):allocation-jack.jar)
+
diff --git a/java-allocation-instrumenter/COPYING b/java-allocation-instrumenter/COPYING
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/java-allocation-instrumenter/COPYING
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/java-allocation-instrumenter/NOTICE b/java-allocation-instrumenter/NOTICE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/java-allocation-instrumenter/NOTICE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/java-allocation-instrumenter/README.android b/java-allocation-instrumenter/README.android
new file mode 100644
index 0000000..4609678
--- /dev/null
+++ b/java-allocation-instrumenter/README.android
@@ -0,0 +1,4 @@
+URL: https://code.google.com/p/java-allocation-instrumenter/
+Version: 2.1r30
+License: Apache 2.O
+Description: "The Allocation Instrumenter is a Java agent written using the java.lang.instrument API and ASM. Each allocation in your Java program is instrumented; a user-defined callback is invoked on each allocation."
diff --git a/java-allocation-instrumenter/build.xml b/java-allocation-instrumenter/build.xml
new file mode 100644
index 0000000..1a2a001
--- /dev/null
+++ b/java-allocation-instrumenter/build.xml
@@ -0,0 +1,65 @@
+<project name="AllocationInstrumenter" basedir=".">
+ <description>
+ Builds the allocation instrumenter Java agent.
+ </description>
+ <property name="src" location="java-allocation-instrumenter/src"/>
+ <property name="build" location="build"/>
+ <property name="dist" location="dist"/>
+
+ <target name="init">
+ <mkdir dir="${build}"/>
+ <mkdir dir="${dist}"/>
+ </target>
+
+ <path id="AllocationInstrumenter.classpath">
+ <fileset dir="lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ </path>
+
+ <target name="base_compile" depends="init" description="basic compile">
+ <javac srcdir="${src}" destdir="${build}" includeantruntime="false">
+ <classpath refid="AllocationInstrumenter.classpath"/>
+ </javac>
+ </target>
+
+ <target name="collect_jar" description="Minimized collection classes">
+ <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
+ classpath="lib/jarjar-1.0.jar" />
+ <jarjar destfile="${dist}/collect.jar">
+ <zipfileset src="lib/guava-r06.jar" />
+ <keep pattern="com.google.common.collect.MapMaker"/>
+ <keep pattern="com.google.common.collect.ForwardingMap"/>
+ </jarjar>
+ </target>
+
+ <target name="dist" depends="base_compile,collect_jar" description="result">
+ <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask"
+ classpath="lib/jarjar-1.0.jar" />
+ <jarjar destfile="${dist}/allocation.jar">
+ <manifest>
+ <attribute name="Boot-Class-Path" value="./allocation.jar"/>
+ <attribute name="Premain-Class" value="com.google.monitoring.runtime.instrumentation.AllocationInstrumenter"/>
+ <attribute name="Can-Redefine-Classes" value="true" />
+ <attribute name="Can-Retransform-Classes" value="true" />
+ <attribute name="Main-Class" value="NotSuitableAsMain" />
+ </manifest>
+ <fileset dir="${build}"/>
+ <zipfileset src="lib/asm-4.0.jar" />
+ <zipfileset src="lib/asm-analysis-4.0.jar" />
+ <zipfileset src="lib/asm-commons-4.0.jar" />
+ <zipfileset src="lib/asm-tree-4.0.jar" />
+ <zipfileset src="lib/asm-util-4.0.jar" />
+ <zipfileset src="lib/asm-xml-4.0.jar" />
+ <zipfileset src="${dist}/collect.jar" />
+ <rule pattern="org.objectweb.asm.**" result="com.google.monitoring.runtime.instrumentation.asm.@1"/>
+ <rule pattern="com.google.common.**" result="com.google.monitoring.runtime.instrumentation.common.@0"/>
+ </jarjar>
+ </target>
+
+ <target name="clean" description="clean up" >
+ <delete dir="${build}"/>
+ <delete dir="${dist}"/>
+ </target>
+
+</project>
diff --git a/java-allocation-instrumenter/etc/manifest.txt b/java-allocation-instrumenter/etc/manifest.txt
new file mode 100644
index 0000000..1f038fb
--- /dev/null
+++ b/java-allocation-instrumenter/etc/manifest.txt
@@ -0,0 +1,5 @@
+Premain-Class: com.google.monitoring.runtime.instrumentation.AllocationInstrumenter
+Boot-Class-Path: ./allocation.jar
+Can-Redefine-Classes: true
+Can-Retransform-Classes: true
+Main-Class: NotSuitableAsMain
diff --git a/java-allocation-instrumenter/jarjar-rules.txt b/java-allocation-instrumenter/jarjar-rules.txt
new file mode 100644
index 0000000..05d00f5
--- /dev/null
+++ b/java-allocation-instrumenter/jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule org.objectweb.asm.** com.google.monitoring.runtime.instrumentation.asm.@1
+rule com.google.common.** com.google.monitoring.runtime.instrumentation.common.@0
diff --git a/java-allocation-instrumenter/lib/asm-4.0.jar b/java-allocation-instrumenter/lib/asm-4.0.jar
new file mode 100644
index 0000000..6d63075
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/asm-analysis-4.0.jar b/java-allocation-instrumenter/lib/asm-analysis-4.0.jar
new file mode 100644
index 0000000..48bb9b8
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-analysis-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/asm-commons-4.0.jar b/java-allocation-instrumenter/lib/asm-commons-4.0.jar
new file mode 100644
index 0000000..8d564b1
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-commons-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/asm-tree-4.0.jar b/java-allocation-instrumenter/lib/asm-tree-4.0.jar
new file mode 100644
index 0000000..aa99d3a
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-tree-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/asm-util-4.0.jar b/java-allocation-instrumenter/lib/asm-util-4.0.jar
new file mode 100644
index 0000000..0e10595
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-util-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/asm-xml-4.0.jar b/java-allocation-instrumenter/lib/asm-xml-4.0.jar
new file mode 100644
index 0000000..fe5d718
--- /dev/null
+++ b/java-allocation-instrumenter/lib/asm-xml-4.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/guava-r06.jar b/java-allocation-instrumenter/lib/guava-r06.jar
new file mode 100644
index 0000000..8ff3a81
--- /dev/null
+++ b/java-allocation-instrumenter/lib/guava-r06.jar
Binary files differ
diff --git a/java-allocation-instrumenter/lib/jarjar-1.0.jar b/java-allocation-instrumenter/lib/jarjar-1.0.jar
new file mode 100644
index 0000000..89390bf
--- /dev/null
+++ b/java-allocation-instrumenter/lib/jarjar-1.0.jar
Binary files differ
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java
new file mode 100644
index 0000000..d66bd15
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationClassAdapter.java
@@ -0,0 +1,56 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.LocalVariablesSorter;
+import org.objectweb.asm.commons.JSRInlinerAdapter;
+
+/**
+ * Instruments bytecodes that allocate heap memory to call a recording hook.
+ * A <code>ClassVisitor</code> that processes methods with a
+ * <code>AllocationMethodAdapter</code> to instrument heap allocations.
+ *
+ * @author jeremymanson@google.com (Jeremy Manson)
+ * @author fischman@google.com (Ami Fischman) (Original Author)
+ */
+class AllocationClassAdapter extends ClassVisitor {
+ private final String recorderClass;
+ private final String recorderMethod;
+
+ public AllocationClassAdapter(ClassVisitor cv, String recorderClass,
+ String recorderMethod) {
+ super(Opcodes.ASM4, cv);
+ this.recorderClass = recorderClass;
+ this.recorderMethod = recorderMethod;
+ }
+
+ /**
+ * For each method in the class being instrumented, <code>visitMethod</code>
+ * is called and the returned MethodVisitor is used to visit the method.
+ * Note that a new MethodVisitor is constructed for each method.
+ */
+ @Override
+ public MethodVisitor visitMethod(int access, String base, String desc,
+ String signature, String[] exceptions) {
+ MethodVisitor mv =
+ cv.visitMethod(access, base, desc, signature, exceptions);
+
+ if (mv != null) {
+ // We need to compute stackmaps (see
+ // AllocationInstrumenter#instrument). This can't really be
+ // done for old bytecode that contains JSR and RET instructions.
+ // So, we remove JSRs and RETs.
+ JSRInlinerAdapter jsria = new JSRInlinerAdapter(
+ mv, access, base, desc, signature, exceptions);
+ AllocationMethodAdapter aimv =
+ new AllocationMethodAdapter(jsria, recorderClass, recorderMethod);
+ LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, aimv);
+ aimv.lvs = lvs;
+ mv = lvs;
+ }
+ return mv;
+ }
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java
new file mode 100644
index 0000000..c4d6ab6
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationInstrumenter.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Instruments bytecodes that allocate heap memory to call a recording hook.
+ * This will add a static invocation to a recorder function to any bytecode that
+ * looks like it will be allocating heap memory allowing users to implement heap
+ * profiling schemes.
+ *
+ * @author Ami Fischman
+ * @author Jeremy Manson
+ */
+public class AllocationInstrumenter implements ClassFileTransformer {
+ static final Logger logger =
+ Logger.getLogger(AllocationInstrumenter.class.getName());
+
+ // We can rewrite classes loaded by the bootstrap class loader
+ // iff the agent is loaded by the bootstrap class loader. It is
+ // always *supposed* to be loaded by the bootstrap class loader, but
+ // this relies on the Boot-Class-Path attribute in the JAR file always being
+ // set to the name of the JAR file that contains this agent, which we cannot
+ // guarantee programmatically.
+ private static volatile boolean canRewriteBootstrap;
+
+ static boolean canRewriteClass(String className, ClassLoader loader) {
+ // There are two conditions under which we don't rewrite:
+ // 1. If className was loaded by the bootstrap class loader and
+ // the agent wasn't (in which case the class being rewritten
+ // won't be able to call agent methods).
+ // 2. If it is java.lang.ThreadLocal, which can't be rewritten because the
+ // JVM depends on its structure.
+ if (((loader == null) && !canRewriteBootstrap) ||
+ className.startsWith("java/lang/ThreadLocal")) {
+ return false;
+ }
+ // third_party/java/webwork/*/ognl.jar contains bad class files. Ugh.
+ if (className.startsWith("ognl/")) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // No instantiating me except in premain() or in {@link JarClassTransformer}.
+ AllocationInstrumenter() { }
+
+ public static void premain(String agentArgs, Instrumentation inst) {
+ AllocationRecorder.setInstrumentation(inst);
+
+ // Force eager class loading here; we need this class to do
+ // instrumentation, so if we don't do the eager class loading, we
+ // get a ClassCircularityError when trying to load and instrument
+ // this class.
+ try {
+ Class.forName("sun.security.provider.PolicyFile");
+ } catch (Throwable t) {
+ // NOP
+ }
+
+ if (!inst.isRetransformClassesSupported()) {
+ System.err.println("Some JDK classes are already loaded and " +
+ "will not be instrumented.");
+ }
+
+ // Don't try to rewrite classes loaded by the bootstrap class
+ // loader if this class wasn't loaded by the bootstrap class
+ // loader.
+ if (AllocationRecorder.class.getClassLoader() != null) {
+ canRewriteBootstrap = false;
+ // The loggers aren't installed yet, so we use println.
+ System.err.println("Class loading breakage: " +
+ "Will not be able to instrument JDK classes");
+ return;
+ }
+
+ canRewriteBootstrap = true;
+
+ inst.addTransformer(new ConstructorInstrumenter(),
+ inst.isRetransformClassesSupported());
+
+ List<String> args = Arrays.asList(
+ agentArgs == null ? new String[0] : agentArgs.split(","));
+ if (!args.contains("manualOnly")) {
+ bootstrap(inst);
+ }
+ }
+
+ private static void bootstrap(Instrumentation inst) {
+ inst.addTransformer(new AllocationInstrumenter(),
+ inst.isRetransformClassesSupported());
+
+ if (!canRewriteBootstrap) {
+ return;
+ }
+
+ // Get the set of already loaded classes that can be rewritten.
+ Class<?>[] classes = inst.getAllLoadedClasses();
+ ArrayList<Class<?>> classList = new ArrayList<Class<?>>();
+ for (int i = 0; i < classes.length; i++) {
+ if (inst.isModifiableClass(classes[i])) {
+ classList.add(classes[i]);
+ }
+ }
+
+ // Reload classes, if possible.
+ Class<?>[] workaround = new Class<?>[classList.size()];
+ try {
+ inst.retransformClasses(classList.toArray(workaround));
+ } catch (UnmodifiableClassException e) {
+ System.err.println("AllocationInstrumenter was unable to " +
+ "retransform early loaded classes.");
+ }
+
+
+ }
+
+ @Override public byte[] transform(
+ ClassLoader loader, String className, Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] origBytes) {
+ if (!canRewriteClass(className, loader)) {
+ return null;
+ }
+
+ return instrument(origBytes, loader);
+ }
+
+ /**
+ * Given the bytes representing a class, go through all the bytecode in it and
+ * instrument any occurences of new/newarray/anewarray/multianewarray with
+ * pre- and post-allocation hooks. Even more fun, intercept calls to the
+ * reflection API's Array.newInstance() and instrument those too.
+ *
+ * @param originalBytes the original <code>byte[]</code> code.
+ * @param recorderClass the <code>String</code> internal name of the class
+ * containing the recorder method to run.
+ * @param recorderMethod the <code>String</code> name of the recorder method
+ * to run.
+ * @return the instrumented <code>byte[]</code> code.
+ */
+ public static byte[] instrument(byte[] originalBytes, String recorderClass,
+ String recorderMethod, ClassLoader loader) {
+ try {
+ ClassReader cr = new ClassReader(originalBytes);
+ // The verifier in JDK7 requires accurate stackmaps, so we use
+ // COMPUTE_FRAMES.
+ ClassWriter cw =
+ new StaticClassWriter(cr, ClassWriter.COMPUTE_FRAMES, loader);
+
+ VerifyingClassAdapter vcw =
+ new VerifyingClassAdapter(cw, originalBytes, cr.getClassName());
+ ClassVisitor adapter =
+ new AllocationClassAdapter(vcw, recorderClass, recorderMethod);
+
+ cr.accept(adapter, ClassReader.SKIP_FRAMES);
+
+ return vcw.toByteArray();
+ } catch (RuntimeException e) {
+ logger.log(Level.WARNING, "Failed to instrument class.", e);
+ throw e;
+ } catch (Error e) {
+ logger.log(Level.WARNING, "Failed to instrument class.", e);
+ throw e;
+ }
+ }
+
+
+ /**
+ * @see #instrument(byte[], String, String, ClassLoader)
+ * documentation for the 4-arg version. This is a convenience
+ * version that uses the recorder in this class.
+ */
+ public static byte[] instrument(byte[] originalBytes, ClassLoader loader) {
+ return instrument(
+ originalBytes,
+ "com/google/monitoring/runtime/instrumentation/AllocationRecorder",
+ "recordAllocation",
+ loader);
+ }
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java
new file mode 100644
index 0000000..eaa0bad
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationMethodAdapter.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.LocalVariablesSorter;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A <code>MethodAdapter</code> that instruments all heap allocation bytecodes
+ * to record the allocation being done for profiling.
+ * Instruments bytecodes that allocate heap memory to call a recording hook.
+ *
+ * @author Ami Fischman
+ */
+class AllocationMethodAdapter extends MethodVisitor {
+ /**
+ * The signature string the recorder method must have. The method must be
+ * static, return void, and take as arguments:
+ * <ol>
+ * <li>an int count of how many instances are being allocated. -1 means a
+ * simple new to distinguish from a 1-element array. 0 shows up as a value
+ * here sometimes; one reason is toArray()-type methods that require an array
+ * type argument (see ArrayList.toArray() for example).</li>
+ * <li>a String descriptor of the class/primitive type being allocated.</li>
+ * <li>an Object reference to the just-allocated Object.</li>
+ * </ol>
+ */
+ public static final String RECORDER_SIGNATURE =
+ "(ILjava/lang/String;Ljava/lang/Object;)V";
+
+ /**
+ * Like RECORDER_SIGNATURE, but for a method that extracts all of
+ * the information dynamically from a class.
+ */
+ public static final String CLASS_RECORDER_SIG =
+ "(Ljava/lang/Class;Ljava/lang/Object;)V";
+
+ // A helper struct for describing the scope of temporary local variables we
+ // create as part of the instrumentation.
+ private static class VariableScope {
+ public final int index;
+ public final Label start;
+ public final Label end;
+ public final String desc;
+ public VariableScope(int index, Label start, Label end, String desc) {
+ this.index = index; this.start = start; this.end = end; this.desc = desc;
+ }
+ }
+
+ // Dictionary of primitive type opcode to English name.
+ private static final String[] primitiveTypeNames = new String[] {
+ "INVALID0", "INVALID1", "INVALID2", "INVALID3",
+ "boolean", "char", "float", "double",
+ "byte", "short", "int", "long"
+ };
+
+ // To track the difference between <init>'s called as the result of a NEW
+ // and <init>'s called because of superclass initialization, we track the
+ // number of NEWs that still need to have their <init>'s called.
+ private int outstandingAllocs = 0;
+
+ // We need to set the scope of any local variables we materialize;
+ // accumulate the scopes here and set them all at the end of the visit to
+ // ensure all labels have been resolved. Allocated on-demand.
+ private List<VariableScope> localScopes = null;
+
+ private List<VariableScope> getLocalScopes() {
+ if (localScopes == null) {
+ localScopes = new LinkedList<VariableScope>();
+ }
+ return localScopes;
+ }
+
+ private final String recorderClass;
+ private final String recorderMethod;
+
+ /**
+ * The LocalVariablesSorter used in this adapter. Lame that it's public but
+ * the ASM architecture requires setting it from the outside after this
+ * AllocationMethodAdapter is fully constructed and the LocalVariablesSorter
+ * constructor requires a reference to this adapter. The only setter of
+ * this should be AllocationClassAdapter.visitMethod().
+ */
+ public LocalVariablesSorter lvs = null;
+
+ /**
+ * A new AllocationMethodAdapter is created for each method that gets visited.
+ */
+ public AllocationMethodAdapter(MethodVisitor mv, String recorderClass,
+ String recorderMethod) {
+ super(Opcodes.ASM4, mv);
+ this.recorderClass = recorderClass;
+ this.recorderMethod = recorderMethod;
+ }
+
+ /**
+ * newarray shows up as an instruction taking an int operand (the primitive
+ * element type of the array) so we hook it here.
+ */
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ if (opcode == Opcodes.NEWARRAY) {
+ // instack: ... count
+ // outstack: ... aref
+ if (operand >= 4 && operand <= 11) {
+ super.visitInsn(Opcodes.DUP); // -> stack: ... count count
+ super.visitIntInsn(opcode, operand); // -> stack: ... count aref
+ invokeRecordAllocation(primitiveTypeNames[operand]);
+ // -> stack: ... aref
+ } else {
+ AllocationInstrumenter.logger.severe("NEWARRAY called with an invalid operand " +
+ operand + ". Not instrumenting this allocation!");
+ super.visitIntInsn(opcode, operand);
+ }
+ } else {
+ super.visitIntInsn(opcode, operand);
+ }
+ }
+
+ // Helper method to compute class name as a String and push it on the stack.
+ // pre: stack: ... class
+ // post: stack: ... class className
+ private void pushClassNameOnStack() {
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... class class
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Class",
+ "getName", "()Ljava/lang/String;");
+ // -> stack: ... class classNameDotted
+ super.visitLdcInsn('.');
+ // -> stack: ... class classNameDotted '.'
+ super.visitLdcInsn('/');
+ // -> stack: ... class classNameDotted '.' '/'
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/String",
+ "replace", "(CC)Ljava/lang/String;");
+ // -> stack: ... class className
+ }
+
+ // Helper method to compute the product of an integer array and push it on
+ // the stack.
+ // pre: stack: ... intArray
+ // post: stack: ... intArray product
+ private void pushProductOfIntArrayOnStack() {
+ Label beginScopeLabel = new Label();
+ Label endScopeLabel = new Label();
+
+ int dimsArrayIndex = newLocal("[I", beginScopeLabel, endScopeLabel);
+ int counterIndex = newLocal("I", beginScopeLabel, endScopeLabel);
+ int productIndex = newLocal("I", beginScopeLabel, endScopeLabel);
+ Label loopLabel = new Label();
+ Label endLabel = new Label();
+
+ super.visitLabel(beginScopeLabel);
+
+ // stack: ... intArray
+ super.visitVarInsn(Opcodes.ASTORE, dimsArrayIndex);
+ // -> stack: ...
+
+ // counter = 0
+ super.visitInsn(Opcodes.ICONST_0);
+ super.visitVarInsn(Opcodes.ISTORE, counterIndex);
+ // product = 1
+ super.visitInsn(Opcodes.ICONST_1);
+ super.visitVarInsn(Opcodes.ISTORE, productIndex);
+ // loop:
+ super.visitLabel(loopLabel);
+ // if index >= arraylength goto end:
+ super.visitVarInsn(Opcodes.ILOAD, counterIndex);
+ super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex);
+ super.visitInsn(Opcodes.ARRAYLENGTH);
+ super.visitJumpInsn(Opcodes.IF_ICMPGE, endLabel);
+ // product = product * max(array[counter],1)
+ super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex);
+ super.visitVarInsn(Opcodes.ILOAD, counterIndex);
+ super.visitInsn(Opcodes.IALOAD);
+ super.visitInsn(Opcodes.DUP);
+ Label nonZeroDimension = new Label();
+ super.visitJumpInsn(Opcodes.IFNE, nonZeroDimension);
+ super.visitInsn(Opcodes.POP);
+ super.visitInsn(Opcodes.ICONST_1);
+ super.visitLabel(nonZeroDimension);
+ super.visitVarInsn(Opcodes.ILOAD, productIndex);
+ super.visitInsn(Opcodes.IMUL); // if overflow happens it happens.
+ super.visitVarInsn(Opcodes.ISTORE, productIndex);
+ // iinc counter 1
+ super.visitIincInsn(counterIndex, 1);
+ // goto loop
+ super.visitJumpInsn(Opcodes.GOTO, loopLabel);
+ // end:
+ super.visitLabel(endLabel);
+ // re-push dimensions array
+ super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex);
+ // push product
+ super.visitVarInsn(Opcodes.ILOAD, productIndex);
+
+ super.visitLabel(endScopeLabel);
+ }
+
+ /**
+ * Reflection-based allocation (@see java.lang.reflect.Array#newInstance) is
+ * triggered with a static method call (INVOKESTATIC), so we hook it here.
+ * Class initialization is triggered with a constructor call (INVOKESPECIAL)
+ * so we hook that here too as a proxy for the new bytecode which leaves an
+ * uninitialized object on the stack that we're not allowed to touch.
+ * {@link java.lang.Object#clone} is also a call to INVOKESPECIAL,
+ * and is hooked here. {@link java.lang.Class#newInstance} and
+ * {@link java.lang.reflect.Constructor#newInstance} are both
+ * INVOKEVIRTUAL calls, so they are hooked here, as well.
+ */
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String signature) {
+ if (opcode == Opcodes.INVOKESTATIC &&
+ // Array does its own native allocation. Grr.
+ owner.equals("java/lang/reflect/Array") &&
+ name.equals("newInstance")) {
+ if (signature.equals("(Ljava/lang/Class;I)Ljava/lang/Object;")) {
+
+ Label beginScopeLabel = new Label();
+ Label endScopeLabel = new Label();
+ super.visitLabel(beginScopeLabel);
+
+ // stack: ... class count
+ int countIndex = newLocal("I", beginScopeLabel, endScopeLabel);
+ super.visitVarInsn(Opcodes.ISTORE, countIndex);
+ // -> stack: ... class
+ pushClassNameOnStack();
+ // -> stack: ... class className
+ int typeNameIndex =
+ newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel);
+ super.visitVarInsn(Opcodes.ASTORE, typeNameIndex);
+ // -> stack: ... class
+ super.visitVarInsn(Opcodes.ILOAD, countIndex);
+ // -> stack: ... class count
+ super.visitMethodInsn(opcode, owner, name, signature);
+ // -> stack: ... newobj
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... newobj newobj
+ super.visitVarInsn(Opcodes.ILOAD, countIndex);
+ // -> stack: ... newobj newobj count
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj count newobj
+ super.visitVarInsn(Opcodes.ALOAD, typeNameIndex);
+ super.visitLabel(endScopeLabel);
+ // -> stack: ... newobj count newobj className
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj count className newobj
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, recorderClass,
+ recorderMethod, RECORDER_SIGNATURE);
+ // -> stack: ... newobj
+ return;
+ } else if (signature.equals("(Ljava/lang/Class;[I)Ljava/lang/Object;")){
+ Label beginScopeLabel = new Label();
+ Label endScopeLabel = new Label();
+ super.visitLabel(beginScopeLabel);
+
+ int dimsArrayIndex = newLocal("[I", beginScopeLabel, endScopeLabel);
+ // stack: ... class dimsArray
+ pushProductOfIntArrayOnStack();
+ // -> stack: ... class dimsArray product
+ int productIndex = newLocal("I", beginScopeLabel, endScopeLabel);
+ super.visitVarInsn(Opcodes.ISTORE, productIndex);
+ // -> stack: ... class dimsArray
+
+ super.visitVarInsn(Opcodes.ASTORE, dimsArrayIndex);
+ // -> stack: ... class
+ pushClassNameOnStack();
+ // -> stack: ... class className
+ int typeNameIndex =
+ newLocal("Ljava/lang/String;", beginScopeLabel, endScopeLabel);
+ super.visitVarInsn(Opcodes.ASTORE, typeNameIndex);
+ // -> stack: ... class
+ super.visitVarInsn(Opcodes.ALOAD, dimsArrayIndex);
+ // -> stack: ... class dimsArray
+ super.visitMethodInsn(opcode, owner, name, signature);
+ // -> stack: ... newobj
+
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... newobj newobj
+ super.visitVarInsn(Opcodes.ILOAD, productIndex);
+ // -> stack: ... newobj newobj product
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj product newobj
+ super.visitVarInsn(Opcodes.ALOAD, typeNameIndex);
+ super.visitLabel(endScopeLabel);
+ // -> stack: ... newobj product newobj className
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj product className newobj
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, recorderClass,
+ recorderMethod, RECORDER_SIGNATURE);
+ // -> stack: ... newobj
+ return;
+ }
+ }
+
+ if (opcode == Opcodes.INVOKEVIRTUAL) {
+ if ("clone".equals(name) && owner.startsWith("[")) {
+ super.visitMethodInsn(opcode, owner, name, signature);
+
+ int i = 0;
+ while (i < owner.length()) {
+ if (owner.charAt(i) != '[') {
+ break;
+ }
+ i++;
+ }
+ if (i > 1) {
+ // -> stack: ... newobj
+ super.visitTypeInsn(Opcodes.CHECKCAST, owner);
+ // -> stack: ... arrayref
+ calculateArrayLengthAndDispatch(owner.substring(i), i);
+ } else {
+ // -> stack: ... newobj
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... newobj newobj
+ super.visitTypeInsn(Opcodes.CHECKCAST, owner);
+ // -> stack: ... newobj arrayref
+ super.visitInsn(Opcodes.ARRAYLENGTH);
+ // -> stack: ... newobj length
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... length newobj
+ invokeRecordAllocation(owner.substring(i));
+ }
+ return;
+ } else if ("newInstance".equals(name)) {
+ if ("java/lang/Class".equals(owner) &&
+ "()Ljava/lang/Object;".equals(signature)) {
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... Class Class
+ super.visitMethodInsn(opcode, owner, name, signature);
+ // -> stack: ... Class newobj
+ super.visitInsn(Opcodes.DUP_X1);
+ // -> stack: ... newobj Class newobj
+ super.visitMethodInsn(Opcodes.INVOKESTATIC,
+ recorderClass, recorderMethod,
+ CLASS_RECORDER_SIG);
+ // -> stack: ... newobj
+ return;
+ } else if ("java/lang/reflect/Constructor".equals(owner) &&
+ "([Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)) {
+ buildRecorderFromObject(opcode, owner, name, signature);
+ return;
+ }
+ }
+ }
+
+ if (opcode == Opcodes.INVOKESPECIAL) {
+ if ("clone".equals(name) && "java/lang/Object".equals(owner)) {
+ buildRecorderFromObject(opcode, owner, name, signature);
+ return;
+ } else if ("<init>".equals(name) && outstandingAllocs > 0) {
+ // Tricky because superclass initializers mean there can be more calls
+ // to <init> than calls to NEW; hence outstandingAllocs.
+ --outstandingAllocs;
+
+ // Most of the time (i.e. in bytecode generated by javac) it is the case
+ // that following an <init> call the top of the stack has a reference ot
+ // the newly-initialized object. But nothing in the JVM Spec requires
+ // this, so we need to play games with the stack to make an explicit
+ // extra copy (and then discard it).
+
+ dupStackElementBeforeSignatureArgs(signature);
+ super.visitMethodInsn(opcode, owner, name, signature);
+ super.visitLdcInsn(-1);
+ super.visitInsn(Opcodes.SWAP);
+ invokeRecordAllocation(owner);
+ super.visitInsn(Opcodes.POP);
+ return;
+ }
+ }
+
+ super.visitMethodInsn(opcode, owner, name, signature);
+ }
+
+ // This is the instrumentation that occurs when there is no static
+ // information about the class we are instantiating. First we build the
+ // object, then we get the class and invoke the recorder.
+ private void buildRecorderFromObject(
+ int opcode, String owner, String name, String signature) {
+ super.visitMethodInsn(opcode, owner, name, signature);
+ // -> stack: ... newobj
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... newobj newobj
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... newobj newobj newobj
+ // We could be instantiating this class or a subclass, so we
+ // have to get the class the hard way.
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/lang/Object",
+ "getClass",
+ "()Ljava/lang/Class;");
+ // -> stack: ... newobj newobj Class
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj Class newobj
+ super.visitMethodInsn(Opcodes.INVOKESTATIC,
+ recorderClass, recorderMethod,
+ CLASS_RECORDER_SIG);
+ // -> stack: ... newobj
+ }
+
+ // Given a method signature interpret the top of the stack as the arguments
+ // to the method, dup the top-most element preceding these arguments, and
+ // leave the arguments alone. This is done by inspecting each parameter
+ // type, popping off the stack elements using the type information,
+ // duplicating the target element, and pushing the arguments back on the
+ // stack.
+ private void dupStackElementBeforeSignatureArgs(final String sig) {
+ final Label beginScopeLabel = new Label();
+ final Label endScopeLabel = new Label();
+ super.visitLabel(beginScopeLabel);
+
+ Type[] argTypes = Type.getArgumentTypes(sig);
+ int[] args = new int[argTypes.length];
+
+ for (int i = argTypes.length - 1; i >= 0; --i) {
+ args[i] = newLocal(argTypes[i], beginScopeLabel, endScopeLabel);
+ super.visitVarInsn(argTypes[i].getOpcode(Opcodes.ISTORE), args[i]);
+ }
+ super.visitInsn(Opcodes.DUP);
+ for (int i = 0; i < argTypes.length; ++i) {
+ super.visitVarInsn(argTypes[i].getOpcode(Opcodes.ILOAD), args[i]);
+ }
+ super.visitLabel(endScopeLabel);
+ }
+
+ /**
+ * new and anewarray bytecodes take a String operand for the type of
+ * the object or array element so we hook them here. Note that new doesn't
+ * actually result in any instrumentation here; we just do a bit of
+ * book-keeping and do the instrumentation following the constructor call
+ * (because we're not allowed to touch the object until it is initialized).
+ */
+ @Override
+ public void visitTypeInsn(int opcode, String typeName) {
+ if (opcode == Opcodes.NEW) {
+ // We can't actually tag this object right after allocation because it
+ // must be initialized with a ctor before we can touch it (Verifier
+ // enforces this). Instead, we just note it and tag following
+ // initialization.
+ super.visitTypeInsn(opcode, typeName);
+ ++outstandingAllocs;
+ } else if (opcode == Opcodes.ANEWARRAY) {
+ super.visitInsn(Opcodes.DUP);
+ super.visitTypeInsn(opcode, typeName);
+ invokeRecordAllocation(typeName);
+ } else {
+ super.visitTypeInsn(opcode, typeName);
+ }
+ }
+
+ /**
+ * Called by the ASM framework once the class is done being visited to
+ * compute stack & local variable count maximums.
+ */
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ if (localScopes != null) {
+ for (VariableScope scope : localScopes) {
+ super.visitLocalVariable("xxxxx$" + scope.index, scope.desc, null,
+ scope.start, scope.end, scope.index);
+ }
+ }
+ super.visitMaxs(maxStack, maxLocals);
+ }
+
+ // Helper method to allocate a new local variable and account for its scope.
+ private int newLocal(Type type, String typeDesc,
+ Label begin, Label end) {
+ int newVar = lvs.newLocal(type);
+ getLocalScopes().add(new VariableScope(newVar, begin, end, typeDesc));
+ return newVar;
+ }
+
+ // Sometimes I happen to have a string descriptor and sometimes a type;
+ // these alternate versions let me avoid recomputing whatever I already
+ // know.
+ private int newLocal(String typeDescriptor, Label begin, Label end) {
+ return newLocal(Type.getType(typeDescriptor), typeDescriptor, begin, end);
+ }
+ private int newLocal(Type type, Label begin, Label end) {
+ return newLocal(type, type.getDescriptor(), begin, end);
+ }
+
+ // Helper method to actually invoke the recorder function for an allocation
+ // event.
+ // pre: stack: ... count newobj
+ // post: stack: ... newobj
+ private void invokeRecordAllocation(String typeName) {
+ typeName = typeName.replaceAll("^\\[*L", "").replaceAll(";$", "");
+ // stack: ... count newobj
+ super.visitInsn(Opcodes.DUP_X1);
+ // -> stack: ... newobj count newobj
+ super.visitLdcInsn(typeName);
+ // -> stack: ... newobj count newobj typename
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... newobj count typename newobj
+ super.visitMethodInsn(Opcodes.INVOKESTATIC,
+ recorderClass, recorderMethod, RECORDER_SIGNATURE);
+ // -> stack: ... newobj
+ }
+
+ /**
+ * multianewarray gets its very own visit method in the ASM framework, so we
+ * hook it here. This bytecode is different from most in that it consumes a
+ * variable number of stack elements during execution. The number of stack
+ * elements consumed is specified by the dimCount operand.
+ */
+ @Override
+ public void visitMultiANewArrayInsn(String typeName, int dimCount) {
+ // stack: ... dim1 dim2 dim3 ... dimN
+ super.visitMultiANewArrayInsn(typeName, dimCount);
+ // -> stack: ... aref
+ calculateArrayLengthAndDispatch(typeName, dimCount);
+ }
+
+ void calculateArrayLengthAndDispatch(String typeName, int dimCount) {
+ // Since the dimensions of the array are not known at instrumentation
+ // time, we take the created multi-dimensional array and peel off nesting
+ // levels from the left. For each nesting layer we probe the array length
+ // and accumulate a partial product which we can then feed the recording
+ // function.
+
+ // below we note the partial product of dimensions 1 to X-1 as productToX
+ // (so productTo1 == 1 == no dimensions yet). We denote by aref0 the
+ // array reference at the current nesting level (the containing aref's [0]
+ // element). If we hit a level whose arraylength is 0 there's no point
+ // continuing so we shortcut out.
+ Label zeroDimension = new Label();
+ super.visitInsn(Opcodes.DUP); // -> stack: ... origaref aref0
+ super.visitLdcInsn(1); // -> stack: ... origaref aref0 productTo1
+ for (int i = 0; i < dimCount; ++i) {
+ // pre: stack: ... origaref aref0 productToI
+ super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref productToI aref
+ super.visitInsn(Opcodes.DUP_X1);
+ // -> stack: ... origaref aref0 productToI aref
+ super.visitInsn(Opcodes.ARRAYLENGTH);
+ // -> stack: ... origaref aref0 productToI dimI
+
+ Label nonZeroDimension = new Label();
+ super.visitInsn(Opcodes.DUP);
+ // -> stack: ... origaref aref0 productToI dimI dimI
+ super.visitJumpInsn(Opcodes.IFNE, nonZeroDimension);
+ // -> stack: ... origaref aref0 productToI dimI
+ super.visitInsn(Opcodes.POP);
+ // -> stack: ... origaref aref0 productToI
+ super.visitJumpInsn(Opcodes.GOTO, zeroDimension);
+ super.visitLabel(nonZeroDimension);
+ // -> stack: ... origaref aref0 productToI max(dimI,1)
+
+ super.visitInsn(Opcodes.IMUL);
+ // -> stack: ... origaref aref0 productTo{I+1}
+ if (i < dimCount - 1) {
+ super.visitInsn(Opcodes.SWAP);
+ // -> stack: ... origaref productTo{I+1} aref0
+ super.visitInsn(Opcodes.ICONST_0);
+ // -> stack: ... origaref productTo{I+1} aref0 0
+ super.visitInsn(Opcodes.AALOAD);
+ // -> stack: ... origaref productTo{I+1} aref0'
+ super.visitInsn(Opcodes.SWAP);
+ }
+ // post: stack: ... origaref aref0 productTo{I+1}
+ }
+ super.visitLabel(zeroDimension);
+
+ super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref product aref0
+ super.visitInsn(Opcodes.POP); // -> stack: ... origaref product
+ super.visitInsn(Opcodes.SWAP); // -> stack: ... product origaref
+ invokeRecordAllocation(typeName);
+ }
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java
new file mode 100644
index 0000000..1ead9ca
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/AllocationRecorder.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+import java.lang.instrument.Instrumentation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import com.google.common.collect.ForwardingMap;
+import com.google.common.collect.MapMaker;
+
+/**
+ * The logic for recording allocations, called from bytecode rewritten by
+ * {@link AllocationInstrumenter}.
+ *
+ * @author jeremymanson@google.com (Jeremy Manson)
+ * @author fischman@google.com (Ami Fischman)
+ */
+public class AllocationRecorder {
+ static {
+ // Sun's JVMs in 1.5.0_06 and 1.6.0{,_01} have a bug where calling
+ // Instrumentation.getObjectSize() during JVM shutdown triggers a
+ // JVM-crashing assert in JPLISAgent.c, so we make sure to not call it after
+ // shutdown. There can still be a race here, depending on the extent of the
+ // JVM bug, but this seems to be good enough.
+ // instrumentation is volatile to make sure the threads reading it (in
+ // recordAllocation()) see the updated value; we could do more
+ // synchronization but it's not clear that it'd be worth it, given the
+ // ambiguity of the bug we're working around in the first place.
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ setInstrumentation(null);
+ }
+ });
+ }
+
+ // See the comment above the addShutdownHook in the static block above
+ // for why this is volatile.
+ private static volatile Instrumentation instrumentation = null;
+
+ static Instrumentation getInstrumentation() {
+ return instrumentation;
+ }
+
+ static void setInstrumentation(Instrumentation inst) {
+ instrumentation = inst;
+ }
+
+ // Mostly because, yes, arrays are faster than collections.
+ private static volatile Sampler [] additionalSamplers;
+
+ // Protects mutations of additionalSamplers. Reads are okay because
+ // the field is volatile, so anyone who reads additionalSamplers
+ // will get a consistent view of it.
+ private static final Object samplerLock = new Object();
+
+ // List of packages that can add samplers.
+ private static final List<String> classNames = new ArrayList<String>();
+
+ static {
+ classNames.add("com.google.monitoring.runtime.");
+ }
+
+ // Used for reentrancy checks
+ private static final ThreadLocal<Boolean> recordingAllocation = new ThreadLocal<Boolean>();
+
+ // Stores the object sizes for the last ~100000 encountered classes
+ private static final ForwardingMap<Class<?>, Long> classSizesMap =
+ new ForwardingMap<Class<?>, Long>() {
+ private final ConcurrentMap<Class<?>, Long> map = new MapMaker()
+ .weakKeys()
+ .makeMap();
+
+ @Override public Map<Class<?>, Long> delegate() {
+ return map;
+ }
+
+ // The approximate maximum size of the map
+ private static final int MAX_SIZE = 100000;
+
+ // The approximate current size of the map; since this is not an AtomicInteger
+ // and since we do not synchronize the updates to this field, it will only be
+ // an approximate size of the map; it's good enough for our purposes though,
+ // and not synchronizing the updates saves us some time
+ private int approximateSize = 0;
+
+ @Override
+ public Long put(Class<?> key, Long value) {
+ // if we have too many elements, delete about 10% of them
+ // this is expensive, but needs to be done to keep the map bounded
+ // we also need to randomize the elements we delete: if we remove the same
+ // elements all the time, we might end up adding them back to the map
+ // immediately after, and then remove them again, then add them back, etc.
+ // which will cause this expensive code to be executed too often
+ if (approximateSize >= MAX_SIZE) {
+ for (Iterator<Class<?>> it = keySet().iterator(); it.hasNext(); ) {
+ it.next();
+ if (Math.random() < 0.1) {
+ it.remove();
+ }
+ }
+
+ // get the exact size; another expensive call, but we need to correct
+ // approximateSize every once in a while, or the difference between
+ // approximateSize and the actual size might become significant over time;
+ // the other solution is synchronizing every time we update approximateSize,
+ // which seems even more expensive
+ approximateSize = size();
+ }
+
+ approximateSize++;
+ return super.put(key, value);
+ }
+ };
+
+ /**
+ * Adds a {@link Sampler} that will get run <b>every time an allocation is
+ * performed from Java code</b>. Use this with <b>extreme</b> judiciousness!
+ *
+ * @param sampler The sampler to add.
+ */
+ public static void addSampler(Sampler sampler) {
+ synchronized (samplerLock) {
+ Sampler[] samplers = additionalSamplers;
+ /* create a new list of samplers from the old, adding this sampler */
+ if (samplers != null) {
+ Sampler [] newSamplers = new Sampler[samplers.length + 1];
+ System.arraycopy(samplers, 0, newSamplers, 0, samplers.length);
+ newSamplers[samplers.length] = sampler;
+ additionalSamplers = newSamplers;
+ } else {
+ Sampler[] newSamplers = new Sampler[1];
+ newSamplers[0] = sampler;
+ additionalSamplers = newSamplers;
+ }
+ }
+ }
+
+ /**
+ * Removes the given {@link Sampler}.
+ *
+ * @param sampler The sampler to remove.
+ */
+ public static void removeSampler(Sampler sampler) {
+ synchronized (samplerLock) {
+ Sampler[] samplers = additionalSamplers;
+ List<Sampler> l = Arrays.asList(samplers);
+ while (l.remove(sampler));
+ additionalSamplers = l.toArray(new Sampler[0]);
+ }
+ }
+
+ /**
+ * Returns the size of the given object. If the object is not an array, we
+ * check the cache first, and update it as necessary.
+ *
+ * @param obj the object.
+ * @param isArray indicates if the given object is an array.
+ * @return the size of the given object.
+ */
+ private static long getObjectSize(Object obj, boolean isArray) {
+ if (isArray) {
+ return instrumentation.getObjectSize(obj);
+ }
+
+ Class<?> clazz = obj.getClass();
+ Long classSize = classSizesMap.get(clazz);
+ if (classSize == null) {
+ classSize = instrumentation.getObjectSize(obj);
+ classSizesMap.put(clazz, classSize);
+ }
+
+ return classSize;
+ }
+
+ public static void recordAllocation(Class<?> cls, Object newObj) {
+ // The use of replace makes calls to this method relatively ridiculously
+ // expensive.
+ String typename = cls.getName().replace(".", "/");
+ recordAllocation(-1, typename, newObj);
+ }
+
+ /**
+ * Records the allocation. This method is invoked on every allocation
+ * performed by the system.
+ *
+ * @param count the count of how many instances are being
+ * allocated, if an array is being allocated. If an array is not being
+ * allocated, then this value will be -1.
+ * @param desc the descriptor of the class/primitive type
+ * being allocated.
+ * @param newObj the new <code>Object</code> whose allocation is being
+ * recorded.
+ */
+ public static void recordAllocation(int count, String desc, Object newObj) {
+ if (recordingAllocation.get() == Boolean.TRUE) {
+ return;
+ } else {
+ recordingAllocation.set(Boolean.TRUE);
+ }
+
+ // NB: This could be smaller if the defaultSampler were merged with the
+ // optional samplers. However, you don't need the optional samplers in
+ // the common case, so I thought I'd save some space.
+ if (instrumentation != null) {
+ // calling getObjectSize() could be expensive,
+ // so make sure we do it only once per object
+ long objectSize = -1;
+
+ Sampler[] samplers = additionalSamplers;
+ if (samplers != null) {
+ if (objectSize < 0) {
+ objectSize = getObjectSize(newObj, (count >= 0));
+ }
+ for (Sampler sampler : samplers) {
+ sampler.sampleAllocation(count, desc, newObj, objectSize);
+ }
+ }
+ }
+
+ recordingAllocation.set(Boolean.FALSE);
+ }
+
+ /**
+ * Helper method to force recording; for unit tests only.
+ */
+ public static void recordAllocationForceForTest(int count, String desc,
+ Object newObj) {
+ // Make sure we get the right number of elided frames
+ recordAllocationForceForTestReal(count, desc, newObj, 2);
+ }
+
+ public static void recordAllocationForceForTestReal(
+ int count, String desc, Object newObj, int recurse) {
+ if (recurse != 0) {
+ recordAllocationForceForTestReal(count, desc, newObj, recurse - 1);
+ return;
+ }
+ }
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java
new file mode 100644
index 0000000..e9fabd3
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+/**
+ * This interface describes a function that is used to sample a
+ * constructor. It is intended to be invoked every time a constructor
+ * for class T is invoked. This will not be invoked when subclasses of
+ * T are instantiated.
+ *
+ * This mechanism works independently of whether the class is part of the
+ * JDK core library.
+ *
+ * @param <T> The class that will be sampled with this ConstructorCallback
+ *
+ * @author Jeremy Manson
+ */
+public interface ConstructorCallback<T> {
+ /**
+ * When an object implementing interface
+ * <code>ConstructorCallback</code> is passed to {@link
+ * com.google.monitoring.runtime.allocation.AllocationInspector#
+ * addConstructorCallback(Class, ConstructorCallback)}, it will get executed
+ * whenever a constructor for type T is invoked.
+ *
+ * @param newObj the new <code>Object</code> whose construction
+ * we're recording. The object is not fully constructed; any
+ * references to this object that are stored in this callback are
+ * subject to the memory model constraints related to such
+ * objects.
+ */
+ public void sample(T newObj);
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java
new file mode 100644
index 0000000..f84e151
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.LocalVariablesSorter;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.security.ProtectionDomain;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Instruments bytecode by inserting a specified call in the
+ * constructor of a given class. This class is intended to be loaded
+ * by a javaagent; end-users will want to add {@link ConstructorCallback}s by
+ * invoking {@link #instrumentClass(Class, ConstructorCallback)}.
+ *
+ * @author Jeremy Manson
+ */
+public class ConstructorInstrumenter implements ClassFileTransformer {
+ // Implementation details: uses the java.lang.instrument API to
+ // insert an INVOKESTATIC call to a specified method directly prior to
+ // constructor return for the given class.
+
+ private static final Logger logger =
+ Logger.getLogger(ConstructorInstrumenter.class.getName());
+ private static ConcurrentHashMap<Class<?>, List<ConstructorCallback<?>>>
+ samplerMap =
+ new ConcurrentHashMap<Class<?>, List<ConstructorCallback<?>>>();
+
+ /**
+ * We have a read-modify-write operation when doing a put in samplerMap
+ * (above) and retransforming the class. This lock protects multiple threads
+ * from performing that operation concurrently.
+ */
+ private static final Object samplerPutAtomicityLock = new Object();
+
+ // Only for package access (specifically, AllocationInstrumenter)
+ ConstructorInstrumenter() { }
+
+ /**
+ * Ensures that the given sampler will be invoked every time a constructor
+ * for class c is invoked.
+ *
+ * @param c The class to be tracked
+ * @param sampler the code to be invoked when an instance of c is constructed
+ */
+ public static void instrumentClass(Class<?> c, ConstructorCallback<?> sampler)
+ throws UnmodifiableClassException {
+ // IMPORTANT: Don't forget that other threads may be accessing this
+ // class while this code is running. Specifically, the class may be
+ // executed directly after the retransformClasses is called. Thus, we need
+ // to be careful about what happens after the retransformClasses call.
+ synchronized (samplerPutAtomicityLock) {
+ List<ConstructorCallback<?>> list = samplerMap.get(c);
+ if (list == null) {
+ CopyOnWriteArrayList<ConstructorCallback<?>> samplerList =
+ new CopyOnWriteArrayList<ConstructorCallback<?>>();
+ samplerList.add(sampler);
+ samplerMap.put(c, samplerList);
+ Instrumentation inst = AllocationRecorder.getInstrumentation();
+ Class<?>[] cs = new Class<?>[1];
+ cs[0] = c;
+ inst.retransformClasses(c);
+ } else {
+ list.add(sampler);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override public byte[] transform(
+ ClassLoader loader, String className, Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+ if ((classBeingRedefined == null) ||
+ (!samplerMap.containsKey(classBeingRedefined))) {
+ return null;
+ }
+ if (!AllocationInstrumenter.canRewriteClass(className, loader)) {
+ throw new RuntimeException(
+ new UnmodifiableClassException("cannot instrument " + className));
+ }
+ return instrument(classfileBuffer, classBeingRedefined);
+ }
+
+ /**
+ * Given the bytes representing a class, add invocations of the
+ * ConstructorCallback method to the constructor.
+ *
+ * @param originalBytes the original <code>byte[]</code> code.
+ * @return the instrumented <code>byte[]</code> code.
+ */
+ public static byte[] instrument(
+ byte[] originalBytes, Class<?> classBeingRedefined) {
+ try {
+ ClassReader cr = new ClassReader(originalBytes);
+ ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
+ VerifyingClassAdapter vcw =
+ new VerifyingClassAdapter(cw, originalBytes, cr.getClassName());
+ ClassVisitor adapter =
+ new ConstructorClassAdapter(vcw, classBeingRedefined);
+
+ cr.accept(adapter, ClassReader.SKIP_FRAMES);
+
+ return vcw.toByteArray();
+ } catch (RuntimeException e) {
+ logger.log(Level.WARNING, "Failed to instrument class.", e);
+ throw e;
+ } catch (Error e) {
+ logger.log(Level.WARNING, "Failed to instrument class.", e);
+ throw e;
+ }
+ }
+
+ /**
+ * The per-method transformations to make. Really only affects the
+ * <init> methods.
+ */
+ static class ConstructorMethodAdapter extends MethodVisitor {
+ /**
+ * The LocalVariablesSorter used in this adapter. Lame that it's public but
+ * the ASM architecture requires setting it from the outside after this
+ * AllocationMethodAdapter is fully constructed and the LocalVariablesSorter
+ * constructor requires a reference to this adapter. The only setter of
+ * this should be AllocationClassAdapter.visitMethod().
+ */
+ public LocalVariablesSorter lvs = null;
+ Class<?> cl;
+ ConstructorMethodAdapter(MethodVisitor mv, Class<?> cl) {
+ super(Opcodes.ASM4, mv);
+ this.cl = cl;
+ }
+
+ /**
+ * Inserts the appropriate INVOKESTATIC call
+ */
+ @Override public void visitInsn(int opcode) {
+ if ((opcode == Opcodes.ARETURN) ||
+ (opcode == Opcodes.IRETURN) ||
+ (opcode == Opcodes.LRETURN) ||
+ (opcode == Opcodes.FRETURN) ||
+ (opcode == Opcodes.DRETURN)) {
+ throw new RuntimeException(new UnmodifiableClassException(
+ "Constructors are supposed to return void"));
+ }
+ if (opcode == Opcodes.RETURN) {
+ super.visitVarInsn(Opcodes.ALOAD, 0);
+ super.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ "com/google/monitoring/runtime/instrumentation/ConstructorInstrumenter",
+ "invokeSamplers",
+ "(Ljava/lang/Object;)V");
+ }
+ super.visitInsn(opcode);
+ }
+ }
+
+ /**
+ * Bytecode is rewritten to invoke this method; it calls the sampler for
+ * the given class. Note that it won't do anything if o is a subclass
+ * of the class that was supposed to be tracked.
+ */
+ @SuppressWarnings("unchecked")
+ public static void invokeSamplers(Object o) {
+ List<ConstructorCallback<?>> samplers = samplerMap.get(o.getClass());
+ if (samplers != null) {
+ for (@SuppressWarnings("rawtypes") ConstructorCallback sampler : samplers) {
+ sampler.sample(o);
+ }
+ }
+ }
+
+ /**
+ * The class that deals with per-class transformations. Basically, invokes
+ * the per-method transformer above if the method is an {@code <init>} method.
+ */
+ static class ConstructorClassAdapter extends ClassVisitor {
+ Class<?> cl;
+ public ConstructorClassAdapter(ClassVisitor cv, Class<?> cl) {
+ super(Opcodes.ASM4, cv);
+ this.cl = cl;
+ }
+
+ /**
+ * For each method in the class being instrumented,
+ * <code>visitMethod</code> is called and the returned
+ * MethodVisitor is used to visit the method. Note that a new
+ * MethodVisitor is constructed for each method.
+ */
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ MethodVisitor mv =
+ cv.visitMethod(access, name, desc, signature, exceptions);
+
+ if ((mv != null) && "<init>".equals(name)){
+ ConstructorMethodAdapter aimv = new ConstructorMethodAdapter(mv, cl);
+ LocalVariablesSorter lvs = new LocalVariablesSorter(access, desc, aimv);
+ aimv.lvs = lvs;
+ mv = lvs;
+ }
+ return mv;
+ }
+ }
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java
new file mode 100644
index 0000000..35b0a50
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/Sampler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+/**
+ * This interface describes a function that is used to sample an allocation.
+ *
+ * @author jeremymanson@google.com (Jeremy Manson)
+ */
+public interface Sampler {
+ /**
+ * Determines whether the object currently being allocated, with the given
+ * size, should be traced.
+ *
+ * <b>CAUTION: DO NOT DO ALLOCATION IN THIS METHOD WITHOUT ENSURING THAT
+ * THE SAMPLER WILL NOT BE INVOKED ON THE RESULTING ALLOCATION.</b>
+ * Otherwise, you will get an infinite regress of calls to the sampler.
+ *
+ * @param count the <code>int</code> count of how many instances are being
+ * allocated. -1 means a simple new to distinguish from a 1-element array. 0
+ * shows up as a value here sometimes; one reason is T[] toArray()-type
+ * methods that require an array type argument (see ArrayList.toArray() for
+ * example).
+ * @param desc the <code>String</code> descriptor of the class/primitive type
+ * being allocated.
+ * @param newObj the new <code>Object</code> whose allocation we're
+ * recording.
+ * @param size the size of the object being allocated.
+ */
+ public void sampleAllocation(int count, String desc,
+ Object newObj, long size);
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java
new file mode 100644
index 0000000..1e26d4c
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/StaticClassWriter.java
@@ -0,0 +1,273 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Portions Copyright 2011 Google, Inc.
+//
+// This is an extracted version of the ClassInfo and ClassWriter
+// portions of ClassWriterComputeFramesTest in the set of ASM tests.
+// We have done a fair bit of rewriting for readability, and changed
+// the comments. The original author is Eric Bruneton.
+
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * A {@link ClassWriter} that looks for static class data in the
+ * classpath when the classes are not available at runtime.
+ *
+ * <p>ClassWriter uses class hierarchy information, which it gets by
+ * looking at loaded classes, to make some decisions about the best
+ * way to write classes. The problem with this is that it fails if
+ * the superclass hasn't been loaded yet. StaticClassWriter fails
+ * over to looking for the class hierarchy information in the
+ * ClassLoader's resources (usually the classpath) if the class it
+ * needs hasn't been loaded yet.
+ *
+ * <p>This class was heavily influenced by ASM's
+ * org.objectweb.asm.util.ClassWriterComputeFramesTest, which contains
+ * the same logic in a subclass. The code here has been slightly
+ * cleaned up for readability.
+ *
+ * @author jeremymanson@google.com (Jeremy Manson)
+ */
+class StaticClassWriter extends ClassWriter {
+
+ /* The classloader that we use to look for the unloaded class */
+ private final ClassLoader classLoader;
+
+ /**
+ * {@inheritDoc}
+ * @param classLoader the class loader that loaded this class
+ */
+ public StaticClassWriter(
+ ClassReader classReader, int flags, ClassLoader classLoader) {
+ super(classReader, flags);
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override protected String getCommonSuperClass(
+ final String type1, final String type2) {
+ try {
+ return super.getCommonSuperClass(type1, type2);
+ } catch (Throwable e) {
+ // Try something else...
+ }
+ // Exactly the same as in ClassWriter, but gets the superclass
+ // directly from the class file.
+ ClassInfo ci1, ci2;
+ try {
+ ci1 = new ClassInfo(type1, classLoader);
+ ci2 = new ClassInfo(type2, classLoader);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ if (ci1.isAssignableFrom(ci2)) {
+ return type1;
+ }
+ if (ci2.isAssignableFrom(ci1)) {
+ return type2;
+ }
+ if (ci1.isInterface() || ci2.isInterface()) {
+ return "java/lang/Object";
+ }
+
+ do {
+ // Should never be null, because if ci1 were the Object class
+ // or an interface, it would have been caught above.
+ ci1 = ci1.getSuperclass();
+ } while (!ci1.isAssignableFrom(ci2));
+ return ci1.getType().getInternalName();
+ }
+
+ /**
+ * For a given class, this stores the information needed by the
+ * getCommonSuperClass test. This determines if the class is
+ * available at runtime, and then, if it isn't, it tries to get the
+ * class file, and extract the appropriate information from that.
+ */
+ static class ClassInfo {
+
+ private final Type type;
+ private final ClassLoader loader;
+ private final boolean isInterface;
+ private final String superClass;
+ private final String[] interfaces;
+
+ public ClassInfo(String type, ClassLoader loader) {
+ Class cls = null;
+ // First, see if we can extract the information from the class...
+ try {
+ cls = Class.forName(type);
+ } catch (Exception e) {
+ // failover...
+ }
+
+ if (cls != null) {
+ this.type = Type.getType(cls);
+ this.loader = loader;
+ this.isInterface = cls.isInterface();
+ this.superClass = cls.getSuperclass().getName();
+ Class[] ifs = cls.getInterfaces();
+ this.interfaces = new String[ifs.length];
+ for (int i = 0; i < ifs.length; i++) {
+ this.interfaces[i] = ifs[i].getName();
+ }
+ return;
+ }
+
+ // The class isn't loaded. Try to get the class file, and
+ // extract the information from that.
+ this.loader = loader;
+ this.type = Type.getObjectType(type);
+ String fileName = type.replace('.', '/') + ".class";
+ InputStream is = null;
+ ClassReader cr;
+ try {
+ is = (loader == null) ?
+ ClassLoader.getSystemResourceAsStream(fileName) :
+ loader.getResourceAsStream(fileName);
+ cr = new ClassReader(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ int offset = cr.header;
+ isInterface = (cr.readUnsignedShort(offset) & Opcodes.ACC_INTERFACE) != 0;
+ char[] buf = new char[2048];
+
+ // Read the superclass
+ offset += 4;
+ superClass = readConstantPoolString(cr, offset, buf);
+
+ // Read the interfaces
+ offset += 2;
+ int numInterfaces = cr.readUnsignedShort(offset);
+ interfaces = new String[numInterfaces];
+ offset += 2;
+ for (int i = 0; i < numInterfaces; i++) {
+ interfaces[i] = readConstantPoolString(cr, offset, buf);
+ offset += 2;
+ }
+ }
+
+ String readConstantPoolString(ClassReader cr, int offset, char[] buf) {
+ int cpIndex = cr.getItem(cr.readUnsignedShort(offset));
+ if (cpIndex == 0) {
+ return null;
+ // throw new RuntimeException("Bad constant pool index");
+ }
+ return cr.readUTF8(cpIndex, buf);
+ }
+
+ Type getType() {
+ return type;
+ }
+
+ ClassInfo getSuperclass() {
+ if (superClass == null) {
+ return null;
+ }
+ return new ClassInfo(superClass, loader);
+ }
+
+ /**
+ * Same as {@link Class#getInterfaces()}
+ */
+ ClassInfo[] getInterfaces() {
+ if (interfaces == null) {
+ return new ClassInfo[0];
+ }
+ ClassInfo[] result = new ClassInfo[interfaces.length];
+ for (int i = 0; i < result.length; ++i) {
+ result[i] = new ClassInfo(interfaces[i], loader);
+ }
+ return result;
+ }
+
+ /**
+ * Same as {@link Class#isInterface}
+ */
+ boolean isInterface() {
+ return isInterface;
+ }
+
+ private boolean implementsInterface(ClassInfo that) {
+ for (ClassInfo c = this; c != null; c = c.getSuperclass()) {
+ for (ClassInfo iface : c.getInterfaces()) {
+ if (iface.type.equals(that.type) ||
+ iface.implementsInterface(that)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isSubclassOf(ClassInfo that) {
+ for (ClassInfo ci = this; ci != null; ci = ci.getSuperclass()) {
+ if (ci.getSuperclass() != null &&
+ ci.getSuperclass().type.equals(that.type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Same as {@link Class#isAssignableFrom(Class)}
+ */
+ boolean isAssignableFrom(ClassInfo that) {
+ return (this == that ||
+ that.isSubclassOf(this) ||
+ that.implementsInterface(this) ||
+ (that.isInterface()
+ && getType().getDescriptor().equals("Ljava/lang/Object;")));
+ }
+ }
+
+}
diff --git a/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java
new file mode 100644
index 0000000..8e5ca6c
--- /dev/null
+++ b/java-allocation-instrumenter/src/main/java/com/google/monitoring/runtime/instrumentation/VerifyingClassAdapter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.monitoring.runtime.instrumentation;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.CodeSizeEvaluator;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This is a class writer that gets used in place of the existing
+ * {@link ClassWriter}, and verifies properties of the class getting written.
+ *
+ * Currently, it only checks to see if the methods are of the correct length
+ * for Java methods (<64K).
+ *
+ * @author jeremymanson@google.com (Jeremy Manson)
+ */
+public class VerifyingClassAdapter extends ClassVisitor {
+ private static final Logger logger =
+ Logger.getLogger(VerifyingClassAdapter.class.getName());
+
+ /**
+ * An enum which indicates whether the class in question is verified.
+ */
+ public enum State {
+ PASS, UNKNOWN, FAIL_TOO_LONG;
+ }
+
+ final ClassWriter cw;
+ final byte [] original;
+ final String className;
+ String message;
+ State state;
+
+ /**
+ * @param cw A class writer that is wrapped by this class adapter
+ * @param original the original bytecode
+ * @param className the name of the class being examined.
+ */
+ public VerifyingClassAdapter(ClassWriter cw, byte [] original,
+ String className) {
+ super(Opcodes.ASM4, cw);
+ state = State.UNKNOWN;
+ message = "The class has not finished being examined";
+ this.cw = cw;
+ this.original = original;
+ this.className = className.replace('/', '.');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In addition, the returned {@link MethodVisitor} will throw an exception
+ * if the method is greater than 64K in length.
+ */
+ @Override
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ final String signature,
+ final String[] exceptions) {
+ MethodVisitor mv =
+ super.visitMethod(access, name, desc, signature, exceptions);
+ return new CodeSizeEvaluator(mv) {
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ if (getMaxSize() > 64 * 1024) {
+ state = State.FAIL_TOO_LONG;
+ message = "the method " + name + " was too long.";
+ }
+ }
+ };
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ if (state == State.UNKNOWN) {
+ state = State.PASS;
+ }
+ }
+
+ /**
+ * Gets the verification state of this class.
+ *
+ * @return true iff the class passed inspection.
+ */
+ public boolean isVerified() {
+ return state == State.PASS;
+ }
+
+ /**
+ * Returns the byte array that contains the byte code for this class.
+ *
+ * @return a byte array.
+ */
+ public byte[] toByteArray() {
+ if (state != State.PASS) {
+ logger.log(Level.WARNING,
+ "Failed to instrument class " + className + " because " + message);
+ return original;
+ }
+ return cw.toByteArray();
+ }
+}