Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6656: Allow capturing field values with path syntax #20

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7462429
WIP: BCI agent path-syntax evaluation
tabjy Nov 27, 2019
1964111
WIP: fix loading "this"
tabjy Nov 29, 2019
5e46ed6
WIP: avoid loading instrumentation pending classes with parent loader
tabjy Nov 29, 2019
ad7d64f
WIP: enforce access checks on reference chains
tabjy Nov 29, 2019
b58a44e
WIP: implement implicit upwards reference in a nest
tabjy Dec 3, 2019
3f2f402
WIP: syntax parsing with state machine
tabjy Dec 4, 2019
036b324
WIP: state machine for parsing expression
tabjy Dec 11, 2019
4cc7b9e
WIP: add check for access and static context. improves error messages
tabjy Dec 11, 2019
af33e32
implemented path-syntax evaluation
tabjy Dec 12, 2019
a803e7e
remove unnecessary null checks
tabjy Dec 12, 2019
040a42d
add final modifiers
tabjy Dec 13, 2019
db3d626
WIP: refactor
tabjy Dec 16, 2019
92b9250
add a QualifiedThisReference only when making qualified .this or .super
tabjy Dec 16, 2019
e93a31e
refactor error handling. cache resolved reference chain
tabjy Dec 16, 2019
1c7b0ac
rename "watch" to "field"
tabjy Dec 16, 2019
1019ca0
change indentations to using tabs. add license headers
tabjy Dec 16, 2019
e05ea8b
support legacy JFR api
tabjy Dec 17, 2019
5c19b76
add test cases for field capture feature
tabjy Dec 17, 2019
a3e0c61
error on private member access between nestmates
tabjy Dec 17, 2019
3383c08
Merge branch 'master' into agent-path-expression
tabjy Dec 17, 2019
caaa7e7
resolve conflicts after merging from master
tabjy Dec 17, 2019
9782c75
update license header year
tabjy Dec 19, 2019
c35788f
add javadoc
tabjy Dec 19, 2019
c78ce94
lazy-load class with InspectionClassLoader
tabjy Dec 19, 2019
0418566
Merge remote-tracking branch 'upstream/master' into agent-path-expres…
tabjy Jan 10, 2020
4bb163b
update TestSetTransforms with new changes
tabjy Jan 10, 2020
09abbb9
remove unused function
tabjy Jan 13, 2020
10e48d5
update license header year values
tabjy Jan 14, 2020
5dc43be
Merge remote-tracking branch 'upstream/master' into agent-path-expres…
tabjy Jan 17, 2020
a207b81
lazy load inspection class only when needed for field evaluations
tabjy Feb 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
*
* or the following license:
*
* 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 holder 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 HOLDER 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.
*/
package org.openjdk.jmc.agent;

public interface Attribute {
String getName();

String getFieldName();

String getDescription();

String getContentType();

String getRelationKey();

String getConverterClassName();
}
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The contents of this file are subject to the terms of either the Universal Permissive License
* v 1.0 as shown at http://oss.oracle.com/licenses/upl
*
* or the following license:
*
* 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 holder 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 HOLDER 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.
*/
package org.openjdk.jmc.agent;

import org.openjdk.jmc.agent.util.expression.ExpressionResolver;
import org.openjdk.jmc.agent.util.expression.IllegalSyntaxException;
import org.openjdk.jmc.agent.util.expression.ReferenceChain;
import org.openjdk.jmc.agent.util.TypeUtils;

public class Field implements Attribute {

private final String name;
private final String expression;
private final String fieldName;
private final String description;
private final String contentType;
private final String relationKey;
private final String converterClassName;

private Class<?> resolvingCaller;
private ReferenceChain referenceChain;

public Field(String name, String expression, String description, String contentType, String relationKey,
String converterClassName) {
this.name = name;
this.expression = expression;
this.description = description;
this.contentType = contentType;
this.relationKey = relationKey;
this.converterClassName = converterClassName;
this.fieldName = "field" + TypeUtils.deriveIdentifierPart(name);
}

@Override
public String getName() {
return this.name;
}

public String getExpression() {
return expression;
}

@Override
public String getFieldName() {
return this.fieldName;
}

@Override
public String getDescription() {
return this.description;
}

@Override
public String getContentType() {
return this.contentType;
}

@Override
public String getRelationKey() {
return this.relationKey;
}

@Override
public String getConverterClassName() {
return this.converterClassName;
}

public ReferenceChain resolveReferenceChain(Class<?> callerClass) throws IllegalSyntaxException {
if (!callerClass.equals(resolvingCaller)) {
resolvingCaller = callerClass;
referenceChain = ExpressionResolver.solve(callerClass, expression);
}

return referenceChain;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -37,7 +37,7 @@
/**
* Metadata for a parameter to be logged by the agent.
*/
public final class Parameter {
public final class Parameter implements Attribute {
public static final int INDEX_INVALID = -1;

private final int index;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -37,16 +37,20 @@
/**
* Metadata for a return value to be logged by the agent.
*/
public final class ReturnValue {
public final class ReturnValue implements Attribute {
private final String name;
private final String fieldName;
private final String description;
private final String contentType;
private final String relationKey;
private final String converterClassName;

public ReturnValue(String name, String description, String contentType) {
public ReturnValue(String name, String description, String contentType, String relationKey, String converterClassName) {
this.name = name == null ? "Return Value" : name;
this.description = description;
this.contentType = contentType;
this.relationKey = relationKey;
this.converterClassName = converterClassName;
this.fieldName = "field" + TypeUtils.deriveIdentifierPart(this.name); //$NON-NLS-1$
}

Expand All @@ -62,6 +66,16 @@ public String getContentType() {
return contentType;
}

@Override
public String getRelationKey() {
return relationKey;
}

@Override
public String getConverterClassName() {
return converterClassName;
}

public String getFieldName() {
return fieldName;
}
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -105,8 +105,8 @@ protected String getTransformationAttribute(String attribute) {
* @return the instantiated {@link TransformDescriptor}.
*/
public static TransformDescriptor create(
String id, String internalName, Method method, Map<String, String> values, List<Parameter> parameters, ReturnValue returnValue) {
return new JFRTransformDescriptor(id, internalName, method, values, parameters, returnValue);
String id, String internalName, Method method, Map<String, String> values, List<Parameter> parameters, ReturnValue returnValue, List<Field> fields) {
return new JFRTransformDescriptor(id, internalName, method, values, parameters, returnValue, fields);
}

@Override
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
Expand Down Expand Up @@ -47,6 +47,7 @@
import org.openjdk.jmc.agent.jfr.VersionResolver.JFRVersion;
import org.openjdk.jmc.agent.jfr.impl.JFRClassVisitor;
import org.openjdk.jmc.agent.jfrnext.impl.JFRNextClassVisitor;
import org.openjdk.jmc.agent.util.InspectionClassLoader;

public class Transformer implements ClassFileTransformer {
private TransformRegistry registry;
Expand All @@ -62,41 +63,50 @@ public byte[] transform(
if (!registry.hasPendingTransforms(className)) {
return registry.isRevertIntrumentation() ? classfileBuffer : null;
}
return doTransforms(registry.getTransformData(className), classfileBuffer, loader, protectionDomain);

// We need a class instance for reflective inspection, so create a InspectionClassLoader if the class if not yet
// loaded.
return doTransforms(registry.getTransformData(className), classfileBuffer, loader, classBeingRedefined,
protectionDomain, classBeingRedefined != null ? null : new InspectionClassLoader(loader));
}

private byte[] doTransforms(
List<TransformDescriptor> transformDataList, byte[] classfileBuffer, ClassLoader definingClassLoader,
ProtectionDomain protectionDomain) {
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
InspectionClassLoader inspectionClassLoader) {
for (TransformDescriptor td : transformDataList) {
if (td.isPendingTransforms()) {
// FIXME: Optimization, should do all transforms to one class in one go, instead of creating one class writer per transform.
classfileBuffer = doTransform(td, classfileBuffer, definingClassLoader, protectionDomain);
classfileBuffer = doTransform(td, classfileBuffer, definingClassLoader, classBeingRedefined,
protectionDomain, inspectionClassLoader);
td.setPendingTransforms(false);
}
}
return classfileBuffer;
}

private byte[] doTransform(
TransformDescriptor td, byte[] classfileBuffer, ClassLoader definingClassLoader,
ProtectionDomain protectionDomain) {
return doJFRLogging((JFRTransformDescriptor) td, classfileBuffer, definingClassLoader, protectionDomain);
TransformDescriptor td, byte[] classfileBuffer, ClassLoader definingClassLoader, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, InspectionClassLoader inspectionClassLoader) {
return doJFRLogging((JFRTransformDescriptor) td, classfileBuffer, definingClassLoader, classBeingRedefined,
protectionDomain, inspectionClassLoader);
}

private byte[] doJFRLogging(
JFRTransformDescriptor td, byte[] classfileBuffer, ClassLoader definingClassLoader,
ProtectionDomain protectionDomain) {
JFRTransformDescriptor td, byte[] classfileBuffer, ClassLoader definingClassLoader, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, InspectionClassLoader inspectionClassLoader) {
if (VersionResolver.getAvailableJFRVersion() == JFRVersion.NONE) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE,
"Could not find JFR classes. Failed to instrument " + td.getMethod().toString()); //$NON-NLS-1$
return classfileBuffer;
}
try {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = VersionResolver.getAvailableJFRVersion() == JFRVersion.JFRNEXT
? new JFRNextClassVisitor(classWriter, td, definingClassLoader, protectionDomain)
: new JFRClassVisitor(classWriter, td, definingClassLoader, protectionDomain);
ClassVisitor visitor = VersionResolver.getAvailableJFRVersion() == JFRVersion.JFRNEXT ?
new JFRNextClassVisitor(classWriter, td, definingClassLoader, classBeingRedefined, protectionDomain,
inspectionClassLoader) :
new JFRClassVisitor(classWriter, td, definingClassLoader, classBeingRedefined, protectionDomain,
inspectionClassLoader);
ClassReader reader = new ClassReader(classfileBuffer);
reader.accept(visitor, 0);
return classWriter.toByteArray();
Expand Down