/* * Copyright (c) 2013-2014, Pierre Laporte * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this work; if not, see <http://www.gnu.org/licenses/>. */ package fr.pingtimeout.tyrion.model; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; import java.io.InputStream; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.junit.Test; public class LocksFileReaderTest { @Test public void should_transform_events_into_accesses_accessible_by_thread() { // Given String events = "" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"enter\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":107,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); Accessor accessor1 = new Accessor(1L, "Thread 1"); Accessor accessor2 = new Accessor(2L, "Thread 2"); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); ObjectUnderLock lock2 = new ObjectUnderLock("java.lang.Object", 5L); Access accessFromThread1ToLock1 = new Access.Builder().enterAt(100).exitAt(101L).by(accessor1).on(lock1).build(); Access accessFromThread2ToLock2 = new Access.Builder().enterAt(102).exitAt(103).by(accessor2).on(lock2).build(); Access accessFromThread1ToLock2 = new Access.Builder().enterAt(103).exitAt(107).by(accessor1).on(lock2).build(); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); Set<Access> thread1CriticalSections = accessReport.retrieveCriticalSectionsFor(accessor1); Set<Access> thread2CriticalSections = accessReport.retrieveCriticalSectionsFor(accessor2); // Then assertThat(thread1CriticalSections).containsOnly(accessFromThread1ToLock1, accessFromThread1ToLock2); assertThat(thread2CriticalSections).containsOnly(accessFromThread2ToLock2); } @Test public void should_transform_events_into_accesses_accessible_by_monitor() { // Given String events = "" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"enter\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":107,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); Accessor accessor1 = new Accessor(1L, "Thread 1"); Accessor accessor2 = new Accessor(2L, "Thread 2"); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); ObjectUnderLock lock2 = new ObjectUnderLock("java.lang.Object", 5L); Access accessFromThread1ToLock1 = new Access.Builder().enterAt(100).exitAt(101).by(accessor1).on(lock1).build(); Access accessFromThread2ToLock2 = new Access.Builder().enterAt(102).exitAt(103).by(accessor2).on(lock2).build(); Access accessFromThread1ToLock2 = new Access.Builder().enterAt(103).exitAt(107).by(accessor1).on(lock2).build(); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); Set<Access> lock1CriticalSections = accessReport.retrieveCriticalSectionsFor(lock1); Set<Access> lock2CriticalSections = accessReport.retrieveCriticalSectionsFor(lock2); // Then assertThat(lock1CriticalSections).containsOnly(accessFromThread1ToLock1); assertThat(lock2CriticalSections).containsOnly(accessFromThread1ToLock2, accessFromThread2ToLock2); } @Test public void should_count_accesses_by_monitor() { // Given String events = "" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"enter\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":107,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":5,\"className\":\"java.lang.Object\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); ObjectUnderLock lock2 = new ObjectUnderLock("java.lang.Object", 5L); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); int accessorsOfLock1 = accessReport.countDifferentAccessorsFor(lock1); int accessorsOfLock2 = accessReport.countDifferentAccessorsFor(lock2); // Then assertThat(accessorsOfLock1).isEqualTo(1); assertThat(accessorsOfLock2).isEqualTo(2); } @Test public void should_detect_frequent_accesses_with_margin() { // Given String events = "" + "{\"enter\":{\"millis\":95,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":96,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":108,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":110,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); Access accessFromThread1At95 = new Access.Builder().enterAt(95).exitAt(96).by(1L, "Thread 1").on(lock1).build(); Access accessFromThread1At100 = new Access.Builder().enterAt(100).exitAt(101).by(1L, "Thread 1").on(lock1).build(); Access accessFromThread2At102 = new Access.Builder().enterAt(102).exitAt(103).by(2L, "Thread 2").on(lock1).build(); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); Set<Access> frequentAccesses = accessReport.retrieveFrequentAccesses(lock1, 4, MILLISECONDS); // Then assertThat(frequentAccesses).containsOnly(accessFromThread1At95, accessFromThread1At100, accessFromThread2At102); } @Test public void should_detect_contended_accesses_with_margin() { // Given String events = "" + "{\"enter\":{\"millis\":95,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":96,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":108,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":110,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); Accessor accessor1 = new Accessor(1L, "Thread 1"); Accessor accessor2 = new Accessor(2L, "Thread 2"); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); Access accessFromThread1At100 = new Access.Builder().enterAt(100).exitAt(101).by(accessor1).on(lock1).build(); Access accessFromThread2At102 = new Access.Builder().enterAt(102).exitAt(103).by(accessor2).on(lock1).build(); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); Set<Access> contentedAccesses = accessReport.retrieveContendedAccesses(lock1, 4, MILLISECONDS); // Then assertThat(contentedAccesses).containsOnly(accessFromThread1At100, accessFromThread2At102); } @Test public void should_detect_contended_accesses_on_all_locks() { // Given String events = "" + "{\"enter\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":101,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":102,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"exit\":{\"millis\":103,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n" + "{\"enter\":{\"millis\":110,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":6,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":111,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":6,\"className\":\"java.lang.Object\"}}}\n" + "{\"enter\":{\"millis\":112,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":6,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":113,\"nanos\":0,\"accessor\":{\"id\":2,\"name\":\"Thread 2\"},\"target\":{\"hashcode\":6,\"className\":\"java.lang.Object\"}}}\n" + "{\"enter\":{\"millis\":121,\"nanos\":0,\"accessor\":{\"id\":3,\"name\":\"Thread 3\"},\"target\":{\"hashcode\":7,\"className\":\"java.lang.Object\"}}}\n" + "{\"exit\":{\"millis\":122,\"nanos\":0,\"accessor\":{\"id\":3,\"name\":\"Thread 3\"},\"target\":{\"hashcode\":7,\"className\":\"java.lang.Object\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); Accessor accessor1 = new Accessor(1L, "Thread 1"); Accessor accessor2 = new Accessor(2L, "Thread 2"); Accessor accessor3 = new Accessor(3L, "Thread 3"); ObjectUnderLock lock1 = new ObjectUnderLock("java.lang.Class", 4L); ObjectUnderLock lock2 = new ObjectUnderLock("java.lang.Object", 6L); ObjectUnderLock lock3 = new ObjectUnderLock("java.lang.Object", 7L); Access accessOfLock1FromThread1At100 = new Access.Builder().enterAt(100).exitAt(101).by(accessor1).on(lock1).build(); Access accessOfLock1FromThread2At102 = new Access.Builder().enterAt(102).exitAt(103).by(accessor2).on(lock1).build(); Access accessOfLock2FromThread1At110 = new Access.Builder().enterAt(110).exitAt(111).by(accessor1).on(lock2).build(); Access accessOfLock2FromThread2At112 = new Access.Builder().enterAt(112).exitAt(113).by(accessor2).on(lock2).build(); // When AccessReport accessReport = new LocksFileReader(inputStream).buildAccessReport(); Map<ObjectUnderLock, Set<Access>> contentedAccesses = accessReport.retrieveAllContendedAccesses(1, MILLISECONDS); // Then assertThat(contentedAccesses.get(lock1)).containsOnly(accessOfLock1FromThread1At100, accessOfLock1FromThread2At102); assertThat(contentedAccesses.get(lock2)).containsOnly(accessOfLock2FromThread1At110, accessOfLock2FromThread2At112); assertThat(contentedAccesses).hasSize(2); } @Test public void should_ignore_entering_event_for_now() { // TODO: this test is going to be removed once we will implement handling for entering event // Given String events = "" + "{\"entering\":{\"millis\":100,\"nanos\":0,\"accessor\":{\"id\":1,\"name\":\"Thread 1\"},\"target\":{\"hashcode\":4,\"className\":\"java.lang.Class\"}}}\n"; InputStream inputStream = IOUtils.toInputStream(events); // When new LocksFileReader(inputStream).buildAccessReport(); // Then // should not throw an exception } }