Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,23 @@ private void configureArrayRecorders() {

private void configureCollectionRecorders() {
CollectionRecorder recorder = (CollectionRecorder) ObjectRecorderRegistry.COLLECTION_RECORDER.getInstance();
recorder.setMode(options.getCollectionsRecordingMode().get());
recorder.setModes(options.getCollectionsRecordingMode().get());
recorder.setMaxElementsToRecord(options.getMaxItemsCollectionsRecordingOption().get());

ListRecorder listRecorder = (ListRecorder) ObjectRecorderRegistry.LIST_RECORDER.getInstance();
listRecorder.setMode(options.getCollectionsRecordingMode().get());
listRecorder.setModes(options.getCollectionsRecordingMode().get());
listRecorder.setMaxElementsToRecord(options.getMaxItemsCollectionsRecordingOption().get());

SetRecorder setRecorder = (SetRecorder) ObjectRecorderRegistry.SET_RECORDER.getInstance();
setRecorder.setMode(options.getCollectionsRecordingMode().get());
setRecorder.setModes(options.getCollectionsRecordingMode().get());
setRecorder.setMaxElementsToRecord(options.getMaxItemsCollectionsRecordingOption().get());

QueueRecorder queueRecorder = (QueueRecorder) ObjectRecorderRegistry.QUEUE_RECORDER.getInstance();
queueRecorder.setMode(options.getCollectionsRecordingMode().get());
queueRecorder.setModes(options.getCollectionsRecordingMode().get());
queueRecorder.setMaxElementsToRecord(options.getMaxItemsCollectionsRecordingOption().get());

MapRecorder mapRecorder = (MapRecorder) ObjectRecorderRegistry.MAP_RECORDER.getInstance();
mapRecorder.setMode(options.getCollectionsRecordingMode().get());
mapRecorder.setModes(options.getCollectionsRecordingMode().get());
mapRecorder.setMaxEntriesToRecord(options.getMaxItemsCollectionsRecordingOption().get());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class RecordingThreadLocalContext {
private RecordingEventBuffer eventBuffer;

public RecordingThreadLocalContext(AgentOptions options, TypeResolver typeResolver) {
if (options.getCollectionsRecordingMode().get() == CollectionsRecordingMode.NONE && !options.getArraysRecordingOption().get()) {
if (CollectionsRecordingMode.isDisabled(options.getCollectionsRecordingMode().get()) && !options.getArraysRecordingOption().get()) {
this.objectConverter = PassByRefRecordedObjectConverter.INSTANCE;
} else {
this.objectConverter = new ByTypeRecordedObjectConverter(typeResolver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.Collections;
import java.util.List;

import static java.util.Collections.singletonList;

/**
* Agent options which define what packages to instrument, at which method recording should start, etc.
* It's only possible to set settings via JMV system properties at the time.
Expand Down Expand Up @@ -112,13 +114,15 @@ public class AgentOptions {
"Value 'delay:X' allows to set delay after which recording can start. X is specified in seconds. For example, 'delay:60'. " +
"Value 'api' makes the agent behaviour controllable through remote Grpc API."
);
private final AgentOption<CollectionsRecordingMode> collectionsRecordingMode = new AgentOption<>(
private final AgentOption<List<CollectionsRecordingMode>> collectionsRecordingMode = new AgentOption<>(
RECORD_COLLECTIONS_PROPERTY,
CollectionsRecordingMode.NONE,
CollectionsRecordingMode::valueOf,
"Defines if collections, maps and arrays should be recorded. Defaults to 'NONE' which allows the agent to pass all objects by reference" +
" to the background thread. 'JAVA' enables recording of Java standard library collections, maps and arrays. 'ALL' " +
"will record all collections (event 3rd party library collections) which might be very unpleasant, so use with care."
singletonList(CollectionsRecordingMode.NONE),
new ListParser<>(CollectionsRecordingMode::valueOf),
"Defines if collections, maps and arrays should be recorded. " +
"Defaults to 'NONE' which allows the agent to pass all objects by reference to the background thread. " +
"'JAVA' enables recording of Java standard library collections, maps and arrays. " +
"'KT' enables recording of Kotlin standard library collections. " +
"'ALL' will record all collections (event 3rd party library collections) which may incur side effects, so use with care."
);
private final AgentOption<Integer> maxItemsCollectionsRecordingOption = new AgentOption<>(
RECORD_COLLECTIONS_MAX_ITEMS_PROPERTY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@

class ListRecorderTest extends AbstractInstrumentationTest {

@Test
void shouldRecordEmptyList() {
CallRecord root = runSubprocessAndReadFile(
new ForkProcessBuilder()
.withMain(TestCase.class)
.withMethodToRecord(MethodMatcher.parse("**.CollectionsTestKt.getEmptyList"))
.withRecordCollections(CollectionsRecordingMode.JDK, CollectionsRecordingMode.KT)
);

CollectionRecord collection = (CollectionRecord) root.getReturnValue();

assertEquals(0, collection.getSize());
}

@Test
void shouldRecordImmutableListEntries() {
CallRecord root = runSubprocessAndReadFile(
Expand All @@ -40,6 +54,26 @@ void shouldRecordImmutableListEntries() {
assertThat(elements.get(2), RecordingMatchers.isString("EFG"));
}

@Test
void shouldRecordArrayDequeue() {
CallRecord root = runSubprocessAndReadFile(
new ForkProcessBuilder()
.withMain(TestCase.class)
.withMethodToRecord(MethodMatcher.parse("**.CollectionsTestKt.getArrayDequeue"))
.withRecordCollections(CollectionsRecordingMode.JDK, CollectionsRecordingMode.KT)
);

CollectionRecord collection = (CollectionRecord) root.getReturnValue();

assertEquals(5, collection.getSize());
List<ObjectRecord> elements = collection.getElements();
assertEquals(3, elements.size());

assertThat(elements.get(0), RecordingMatchers.isString("A"));
assertThat(elements.get(1), RecordingMatchers.isString("B"));
assertThat(elements.get(2), RecordingMatchers.isString("C"));
}

@Test
void shouldRecordMutableListEntries() {
CallRecord root = runSubprocessAndReadFile(
Expand Down Expand Up @@ -78,7 +112,9 @@ static class TestCase {

public static void main(String[] args) {
System.out.println(CollectionsTestKt.getImmutableList());
System.out.println(CollectionsTestKt.getEmptyList());
System.out.println(CollectionsTestKt.getMutableList());
System.out.println(CollectionsTestKt.getArrayDequeue());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ForkProcessBuilder {

Expand All @@ -22,7 +23,7 @@ public class ForkProcessBuilder {
private List<String> instrumentedPackages = new ArrayList<>();
private String excludeClassesProperty = null;
private List<String> excludedFromInstrumentationPackages = new ArrayList<>();
private CollectionsRecordingMode collectionsRecordingMode;
private List<CollectionsRecordingMode> collectionsRecordingModes;
private String printTypes = null;
private String logLevel = "INFO";
private Boolean agentDisabled = null;
Expand All @@ -45,8 +46,8 @@ public ForkProcessBuilder withMain(Class<?> mainClass) {
return this;
}

public ForkProcessBuilder withRecordCollections(CollectionsRecordingMode mode) {
collectionsRecordingMode = mode;
public ForkProcessBuilder withRecordCollections(CollectionsRecordingMode... modes) {
collectionsRecordingModes = Arrays.asList(modes);
return this;
}

Expand Down Expand Up @@ -166,8 +167,12 @@ public List<String> toCmdJavaProps() {
if (recordCollectionItems != null) {
params.add("-D" + AgentOptions.RECORD_COLLECTIONS_MAX_ITEMS_PROPERTY + "=" + recordCollectionItems);
}
if (collectionsRecordingMode != null) {
params.add("-D" + AgentOptions.RECORD_COLLECTIONS_PROPERTY + "=" + collectionsRecordingMode.name());
if (collectionsRecordingModes != null) {
params.add("-D" + AgentOptions.RECORD_COLLECTIONS_PROPERTY + "=" +
collectionsRecordingModes.stream()
.map(CollectionsRecordingMode::name)
.collect(Collectors.joining(","))
);
}

params.add("-Dulyp.recording-queue.serialization-buffer-size=" + 2048);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ fun getImmutableList(): List<String> {
return listOf("ABC", "CDE", "EFG", "FGH", "HJK")
}

fun getEmptyList(): List<String> {
return emptyList()
}

fun getArrayDequeue(): ArrayDeque<String> {
val deq = ArrayDeque<String>(1)
deq.add("A")
deq.add("B")
deq.add("C")
deq.add("E")
deq.add("F")
return deq
}

fun getMutableList(): MutableList<String> {
return mutableListOf("ABC", "CDE", "EFG", "FGH", "HJK")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
import org.jetbrains.annotations.NotNull;

import javax.annotation.concurrent.ThreadSafe;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.*;

@Slf4j
@ThreadSafe
Expand All @@ -30,25 +27,25 @@ public class CollectionRecorder extends ObjectRecorder {
private volatile boolean active = true;
@Setter
private int maxElementsToRecord;
private CollectionsRecordingMode mode = CollectionsRecordingMode.NONE;
private List<CollectionsRecordingMode> modes = Collections.singletonList(CollectionsRecordingMode.NONE);

public CollectionRecorder(byte id) {
super(id);
}

@Override
public boolean supports(Class<?> type) {
return mode.supports(type) && Collection.class.isAssignableFrom(type);
return modes.stream().anyMatch(mode -> mode.supports(type)) && Collection.class.isAssignableFrom(type);
}

@Override
public boolean supportsAsyncRecording() {
return false;
}

public void setMode(CollectionsRecordingMode mode) {
this.mode = mode;
log.info("Set collection recording mode to {}", mode);
public void setModes(List<CollectionsRecordingMode> modes) {
this.modes = modes;
log.info("Set collection recording mode to {}", modes);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.ulyp.core.recorders.collections;

import java.util.List;

public enum CollectionsRecordingMode {

/**
Expand All @@ -18,6 +20,20 @@ public String toString() {
return "Recording java.* collections";
}
},
/**
* Records all kotlin standard library collections
*/
KT {
@Override
public boolean supports(Class<?> type) {
return type.getName().startsWith("kotlin.");
}

@Override
public String toString() {
return "Recording java.* collections";
}
},
/**
* The most intrusive mode: try to record elements on every collection. Might break things
*/
Expand Down Expand Up @@ -48,4 +64,8 @@ public String toString() {
};

public abstract boolean supports(Class<?> type);

public static boolean isDisabled(List<CollectionsRecordingMode> modes) {
return modes.size() == 1 && modes.get(0) == NONE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import org.jetbrains.annotations.NotNull;

import javax.annotation.concurrent.ThreadSafe;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;

@ThreadSafe
public class MapRecorder extends ObjectRecorder {
Expand All @@ -27,7 +24,7 @@ public class MapRecorder extends ObjectRecorder {
@Setter
private int maxEntriesToRecord;
@Setter
private volatile CollectionsRecordingMode mode = CollectionsRecordingMode.NONE;
private List<CollectionsRecordingMode> modes = Collections.singletonList(CollectionsRecordingMode.NONE);
private volatile boolean active = true;

public MapRecorder(byte id) {
Expand All @@ -36,7 +33,7 @@ public MapRecorder(byte id) {

@Override
public boolean supports(Class<?> type) {
return mode.supports(type) && Map.class.isAssignableFrom(type);
return modes.stream().anyMatch(mode -> mode.supports(type)) && Map.class.isAssignableFrom(type);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package com.ulyp.core.recorders.collections;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;

import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.api.Assertions;
Expand Down Expand Up @@ -52,7 +48,7 @@ void test() throws Exception {
map.put("ABC", new XYZ());
map.put("ZXC", new XYZ());

mapRecorder.setMode(CollectionsRecordingMode.ALL);
mapRecorder.setModes(Collections.singletonList(CollectionsRecordingMode.ALL));

mapRecorder.write(map, out, typeResolver);

Expand All @@ -76,7 +72,7 @@ public Set<Entry<String, XYZ>> entrySet() {
map.put("ABC", new XYZ());
map.put("ZXC", new XYZ());

mapRecorder.setMode(CollectionsRecordingMode.ALL);
mapRecorder.setModes(Collections.singletonList(CollectionsRecordingMode.ALL));
mapRecorder.write(map, out, typeResolver);

IdentityObjectRecord mapRecord = (IdentityObjectRecord) mapRecorder.read(typeResolver.get(map), in, typeResolver::get);
Expand Down