blob: 94ed1f41291dcbd42dcbffdf4f39f4d4a73d3bea [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cassandra.simulator.asm;
import java.util.HashSet;
import java.util.Set;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static org.apache.cassandra.simulator.asm.TransformationKind.FIELD_NEMESIS;
import static org.apache.cassandra.simulator.asm.TransformationKind.SIGNAL_NEMESIS;
/**
* Insert nemesis points at all obvious thread signalling points (execution and blocking primitive methods),
* as well as to any fields annotated with {@link org.apache.cassandra.utils.Nemesis}.
*
* If the annotated field is an AtomicX or AtomicXFieldUpdater, we insert nemesis points either side of the next
* invocation of
*
* TODO (config): permit Nemesis on a class as well as a field, so as to mark all (at least volatile or atomic) members
*/
class NemesisTransformer extends MethodVisitor
{
private final ClassTransformer transformer;
final NemesisGenerator generator;
final NemesisFieldKind.Selector nemesisFieldSelector;
// for simplicity, we simply activate nemesis for all atomic operations on the relevant type once any such
// field is loaded in a method
Set<String> onForTypes;
public NemesisTransformer(ClassTransformer transformer, int api, String name, MethodVisitor parent, NemesisGenerator generator, NemesisFieldKind.Selector nemesisFieldSelector)
{
super(api, parent);
this.transformer = transformer;
this.generator = generator;
this.nemesisFieldSelector = nemesisFieldSelector;
generator.newMethod(name);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)
{
boolean nemesisAfter = false;
if (isInterface && opcode == Opcodes.INVOKEINTERFACE
&& (owner.startsWith("org/apache/cassandra/concurrent") || owner.startsWith("org/apache/cassandra/utils/concurrent")) && (
(owner.equals("org/apache/cassandra/utils/concurrent/CountDownLatch") && name.equals("decrement"))
|| (owner.equals("org/apache/cassandra/utils/concurrent/Condition") && name.equals("signal"))
|| (owner.equals("org/apache/cassandra/utils/concurrent/Semaphore") && name.equals("release"))
|| ((owner.equals("org/apache/cassandra/concurrent/ExecutorPlus")
|| owner.equals("org/apache/cassandra/concurrent/LocalAwareExecutorPlus")
|| owner.equals("org/apache/cassandra/concurrent/ScheduledExecutorPlus")
|| owner.equals("org/apache/cassandra/concurrent/SequentialExecutorPlus")
|| owner.equals("org/apache/cassandra/concurrent/LocalAwareSequentialExecutorPlus")
) && (name.equals("execute") || name.equals("submit") || name.equals("maybeExecuteImmediately")))
))
{
generateAndCall(SIGNAL_NEMESIS);
}
else if ((opcode == Opcodes.INVOKESPECIAL || opcode == Opcodes.INVOKEVIRTUAL)
&& (onForTypes != null && onForTypes.contains(owner)))
{
nemesisAfter = true;
generateAndCall(FIELD_NEMESIS);
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
if (nemesisAfter)
generateAndCall(FIELD_NEMESIS);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor)
{
boolean nemesisAfter = false;
NemesisFieldKind nemesis = nemesisFieldSelector.get(owner, name);
if (nemesis != null)
{
switch (nemesis)
{
case SIMPLE:
switch (opcode)
{
default:
throw new AssertionError();
case Opcodes.PUTFIELD:
case Opcodes.PUTSTATIC:
generateAndCall(FIELD_NEMESIS);
break;
case Opcodes.GETFIELD:
case Opcodes.GETSTATIC:
nemesisAfter = true;
}
break;
case ATOMICX:
case ATOMICUPDATERX:
switch (opcode)
{
case Opcodes.GETFIELD:
case Opcodes.GETSTATIC:
if (onForTypes == null)
onForTypes = new HashSet<>();
onForTypes.add(descriptor.substring(1, descriptor.length() - 1));
}
break;
}
}
super.visitFieldInsn(opcode, owner, name, descriptor);
if (nemesisAfter)
generateAndCall(FIELD_NEMESIS);
}
private void generateAndCall(TransformationKind kind)
{
generator.generateAndCall(kind, transformer, mv);
}
}