diff options
author | 2015-12-07 15:02:00 +0000 | |
---|---|---|
committer | 2015-12-07 21:19:32 +0000 | |
commit | 2be2f385c61ec50e356bd98069ad037ad66f128a (patch) | |
tree | 7daaee06daf6a8eecca257b5af240fc1375284c0 /src/test | |
parent | 6c440f20a8a300d63d33f01c66595a9640d57099 (diff) |
Open-sourced tests from lib/profiler.
--
MOS_MIGRATED_REVID=109577273
Diffstat (limited to 'src/test')
4 files changed, 827 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index 367a32192d..19dc9764e7 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -717,6 +717,28 @@ java_test( ) java_test( + name = "profiler-tests", + srcs = glob([ + "profiler/*.java", + ]), + args = ["com.google.devtools.build.lib.AllTests"], + deps = [ + ":foundations_testutil", + ":syntax_testutil", + ":test_runner", + ":testutil", + "//src/main/java/com/google/devtools/build/lib:clock", + "//src/main/java/com/google/devtools/build/lib:profiler-output", + "//src/main/java/com/google/devtools/build/lib:vfs", + "//third_party:guava", + "//third_party:guava-testlib", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( name = "BazelDocumentationTests", size = "medium", srcs = ["packages/BazelDocumentationTest.java"], diff --git a/src/test/java/com/google/devtools/build/lib/profiler/AutoProfilerTest.java b/src/test/java/com/google/devtools/build/lib/profiler/AutoProfilerTest.java new file mode 100644 index 0000000000..6530d07b24 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/profiler/AutoProfilerTest.java @@ -0,0 +1,53 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// 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.devtools.build.lib.profiler; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.devtools.build.lib.profiler.AutoProfiler.ElapsedTimeReceiver; +import com.google.devtools.build.lib.testutil.ManualClock; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.atomic.AtomicLong; + +/** Tests for {@link AutoProfiler}. */ +@RunWith(JUnit4.class) +public class AutoProfilerTest { + + private ManualClock clock; + + @Before + public final void init() { + clock = new ManualClock(); + AutoProfiler.setClock(clock); + } + + @Test + public void simple() { + final AtomicLong elapsedTime = new AtomicLong(); + ElapsedTimeReceiver receiver = new ElapsedTimeReceiver() { + @Override + public void accept(long elapsedTimeNanos) { + elapsedTime.set(elapsedTimeNanos); + } + }; + try (AutoProfiler profiler = AutoProfiler.create(receiver)) { + clock.advanceMillis(42); + } + assertThat(elapsedTime.get()).isEqualTo(42 * 1000 * 1000); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/profiler/ProfilerChartTest.java b/src/test/java/com/google/devtools/build/lib/profiler/ProfilerChartTest.java new file mode 100644 index 0000000000..1c343b27bd --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/profiler/ProfilerChartTest.java @@ -0,0 +1,306 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// 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.devtools.build.lib.profiler; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; + +import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds; +import com.google.devtools.build.lib.profiler.chart.AggregatingChartCreator; +import com.google.devtools.build.lib.profiler.chart.Chart; +import com.google.devtools.build.lib.profiler.chart.ChartBar; +import com.google.devtools.build.lib.profiler.chart.ChartBarType; +import com.google.devtools.build.lib.profiler.chart.ChartColumn; +import com.google.devtools.build.lib.profiler.chart.ChartCreator; +import com.google.devtools.build.lib.profiler.chart.ChartLine; +import com.google.devtools.build.lib.profiler.chart.ChartRow; +import com.google.devtools.build.lib.profiler.chart.ChartVisitor; +import com.google.devtools.build.lib.profiler.chart.Color; +import com.google.devtools.build.lib.profiler.chart.DetailedChartCreator; +import com.google.devtools.build.lib.testutil.FoundationTestCase; +import com.google.devtools.build.lib.testutil.Scratch; +import com.google.devtools.build.lib.testutil.Suite; +import com.google.devtools.build.lib.testutil.TestSpec; +import com.google.devtools.build.lib.util.BlazeClock; +import com.google.devtools.build.lib.vfs.Path; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +/** + * Unit tests for the profiler chart generation. + */ +@TestSpec(size = Suite.MEDIUM_TESTS) +@RunWith(JUnit4.class) +public class ProfilerChartTest extends FoundationTestCase { + private static final int COMMON_CHART_TYPES = ProfilePhase.values().length; + private static final int DETAILED_CHART_TYPES = ProfilerTask.values().length; + private static final int AGGREGATED_CHART_TYPES = 4; + private static final int AGGREGATED_CHART_NO_VFS_TYPES = 3; + + @Test + public void testChartCreators() throws Exception { + Runnable run = new Runnable() { + @Override + public void run() { + Profiler.instance().startTask(ProfilerTask.ACTION, "action"); + Profiler.instance().completeTask(ProfilerTask.ACTION); + } + }; + int threads = 4; // there is one extra thread due due the event that finalizes the profiler + ProfileInfo info = createProfileInfo(run, threads - 1); + ChartCreator aggregatingCreator = new AggregatingChartCreator(info, true); + Chart aggregatedChart = aggregatingCreator.create(); + assertEquals(threads, aggregatedChart.getRowCount()); + assertThat(aggregatedChart.getSortedRows().get(0).getBars()).hasSize(1); + + ChartCreator detailedCreator = new DetailedChartCreator(info); + Chart detailedChart = detailedCreator.create(); + assertThat(detailedChart.getSortedTypes()).hasSize(COMMON_CHART_TYPES + DETAILED_CHART_TYPES); + assertEquals(threads, detailedChart.getRowCount()); + assertThat(detailedChart.getSortedRows().get(0).getBars()).hasSize(1); + } + + @Test + public void testAggregatingChartCreator() throws Exception { + Runnable run = new Runnable() { + @Override + public void run() { + Profiler profiler = Profiler.instance(); + profiler.startTask(ProfilerTask.ACTION, "action"); // Stays + task(profiler, ProfilerTask.REMOTE_EXECUTION, "remote execution"); // Removed + task(profiler, ProfilerTask.ACTION_CHECK, "check"); // Removed + task(profiler, ProfilerTask.ACTION_LOCK, "lock"); // Stays + profiler.completeTask(ProfilerTask.ACTION); + task(profiler, ProfilerTask.INFO, "info"); // Stays + task(profiler, ProfilerTask.VFS_STAT, "stat"); // Stays, if showVFS + task(profiler, ProfilerTask.WAIT, "wait"); // Stays + } + }; + ProfileInfo info = createProfileInfo(run, 1); + + ChartCreator aggregatingCreator = new AggregatingChartCreator(info, true); + Chart aggregatedChart = aggregatingCreator.create(); + assertThat(aggregatedChart.getSortedTypes()) + .hasSize(COMMON_CHART_TYPES + AGGREGATED_CHART_TYPES); + assertThat(aggregatedChart.getSortedRows().get(0).getBars()).hasSize(5); + + ChartCreator aggregatingNoVfsCreator = new AggregatingChartCreator(info, false); + Chart aggregatedNoVfsChart = aggregatingNoVfsCreator.create(); + assertThat(aggregatedNoVfsChart.getSortedTypes()) + .hasSize(COMMON_CHART_TYPES + AGGREGATED_CHART_NO_VFS_TYPES); + assertThat(aggregatedNoVfsChart.getSortedRows().get(0).getBars()).hasSize(4); + + ChartCreator detailedCreator = new DetailedChartCreator(info); + Chart detailedChart = detailedCreator.create(); + assertThat(detailedChart.getSortedTypes()) + .hasSize(COMMON_CHART_TYPES + ProfilerTask.values().length); + assertThat(detailedChart.getSortedRows().get(0).getBars()).hasSize(7); + } + + @Test + public void testChart() throws Exception { + Chart chart = new Chart(); + + ChartBarType type3 = chart.createType("name3", Color.GREEN); + ChartBarType type2 = chart.createType("name2", Color.RED); + ChartBarType type1 = chart.createType("name1", Color.BLACK); + List<ChartBarType> types = chart.getSortedTypes(); + assertThat(types).hasSize(3); + assertEquals(type1.getName(), types.get(0).getName()); + assertEquals(type1.getColor(), types.get(0).getColor()); + assertEquals(type2.getName(), types.get(1).getName()); + assertEquals(type2.getColor(), types.get(1).getColor()); + assertEquals(type3.getName(), types.get(2).getName()); + assertEquals(type3.getColor(), types.get(2).getColor()); + + assertSame(type3, chart.lookUpType("name3")); + assertSame(type2, chart.lookUpType("name2")); + assertSame(type1, chart.lookUpType("name1")); + + assertSame(Chart.UNKNOWN_TYPE, chart.lookUpType("wergl")); + types = chart.getSortedTypes(); + assertThat(types).hasSize(4); + + chart.addBar(1, 2, 3, type1, "label1"); + chart.addBar(2, 3, 4, type2, "label2"); + chart.addBar(2, 4, 5, type3, "label3"); + chart.addBar(3, 3, 4, type2, "label4"); + chart.addBar(3, 4, 5, type3, "label5"); + chart.addBar(3, 5, 6, type3, "label6"); + + assertEquals(6, chart.getMaxStop()); + assertEquals(3, chart.getRowCount()); + + List<ChartRow> rows = chart.getSortedRows(); + assertThat(rows).hasSize(3); + assertThat(rows.get(0).getBars()).hasSize(1); + assertThat(rows.get(1).getBars()).hasSize(2); + assertThat(rows.get(2).getBars()).hasSize(3); + + ChartBar bar = rows.get(0).getBars().get(0); + assertEquals(2, bar.getStart()); + assertEquals(3, bar.getStop()); + assertSame(type1, bar.getType()); + assertEquals("label1", bar.getLabel()); + } + + @Test + public void testChartRows() throws Exception { + ChartRow row1 = new ChartRow("1", 0); + ChartRow row2 = new ChartRow("2", 1); + ChartRow row3 = new ChartRow("3", 1); + + assertEquals("1", row1.getId()); + assertEquals(0, row1.getIndex()); + + assertEquals(-1, row1.compareTo(row2)); + assertEquals(1, row2.compareTo(row1)); + assertEquals(0, row2.compareTo(row3)); + + row1.addBar(new ChartBar(row1, 1, 2, new ChartBarType("name1", Color.BLACK), false, "label1")); + row1.addBar(new ChartBar(row1, 2, 3, new ChartBarType("name2", Color.RED), false, "label2")); + + assertThat(row1.getBars()).hasSize(2); + assertEquals("label1", row1.getBars().get(0).getLabel()); + assertEquals("label2", row1.getBars().get(1).getLabel()); + } + + @Test + public void testChartBarTypes() throws Exception { + ChartBarType type1 = new ChartBarType("name1", Color.BLACK); + ChartBarType type2 = new ChartBarType("name2", Color.RED); + ChartBarType type3 = new ChartBarType("name2", Color.GREEN); + + assertEquals(-1, type1.compareTo(type2)); + assertEquals(1, type2.compareTo(type1)); + assertEquals(0, type2.compareTo(type3)); + + assertEquals(type3, type2); + assertFalse(type1.equals(type3)); + assertFalse(type1.equals(type2)); + + assertEquals(type3.hashCode(), type2.hashCode()); + assertFalse(type1.hashCode() == type2.hashCode()); + assertFalse(type1.hashCode() == type3.hashCode()); + } + + @Test + public void testChartBar() throws Exception { + ChartRow row1 = new ChartRow("1", 0); + ChartBarType type = new ChartBarType("name1", Color.BLACK); + ChartBar bar1 = new ChartBar(row1, 1, 2, type, false, "label1"); + assertEquals(row1, bar1.getRow()); + assertEquals(1, bar1.getStart()); + assertEquals(2, bar1.getStop()); + assertSame(type, bar1.getType()); + assertEquals("label1", bar1.getLabel()); + } + + @Test + public void testVisitor() throws Exception { + Chart chart = new Chart(); + ChartBarType type3 = chart.createType("name3", Color.GREEN); + ChartBarType type2 = chart.createType("name2", Color.RED); + ChartBarType type1 = chart.createType("name1", Color.BLACK); + chart.addBar(1, 2, 3, type1, "label1"); + chart.addBar(2, 3, 4, type2, "label2"); + chart.addBar(2, 4, 5, type3, "label3"); + chart.addBar(3, 3, 4, type2, "label4"); + chart.addBar(3, 4, 5, type3, "label5"); + chart.addBar(3, 5, 6, type3, "label6"); + + TestingChartVisitor visitor = new TestingChartVisitor(); + chart.accept(visitor); + assertEquals(1, visitor.beginChartCount); + assertEquals(1, visitor.endChartCount); + assertEquals(3, visitor.rowCount); + assertEquals(6, visitor.barCount); + assertEquals(0, visitor.columnCount); + assertEquals(0, visitor.lineCount); + } + + private ProfileInfo createProfileInfo(Runnable runnable, int noOfRows) throws Exception { + Scratch scratch = new Scratch(); + Path cacheDir = scratch.dir("/tmp"); + Path cacheFile = cacheDir.getRelative("profile1.dat"); + Profiler profiler = Profiler.instance(); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + + // Write from multiple threads to generate multiple rows in the chart. + for (int i = 0; i < noOfRows; i++) { + Thread t = new Thread(runnable); + t.start(); + t.join(); + } + + profiler.stop(); + return ProfileInfo.loadProfile(cacheFile); + } + + private void task(final Profiler profiler, ProfilerTask task, String name) { + profiler.startTask(task, name); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + profiler.completeTask(task); + } + + private static final class TestingChartVisitor implements ChartVisitor { + private int rowCount; + private int endChartCount; + private int barCount; + private int beginChartCount; + private int columnCount; + private int lineCount; + + @Override + public void visit(Chart chart) { + beginChartCount++; + } + + @Override + public void endVisit(Chart chart) { + endChartCount++; + } + + @Override + public void visit(ChartRow chartRow) { + rowCount++; + } + + @Override + public void visit(ChartBar chartBar) { + barCount++; + } + + @Override + public void visit(ChartColumn chartColumn) { + columnCount++; + } + + @Override + public void visit(ChartLine chartLine) { + lineCount++; + } + } +} diff --git a/src/test/java/com/google/devtools/build/lib/profiler/ProfilerTest.java b/src/test/java/com/google/devtools/build/lib/profiler/ProfilerTest.java new file mode 100644 index 0000000000..422ae46a79 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/profiler/ProfilerTest.java @@ -0,0 +1,446 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// 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.devtools.build.lib.profiler; + +import static com.google.common.truth.Truth.assertThat; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds; +import com.google.devtools.build.lib.testutil.FoundationTestCase; +import com.google.devtools.build.lib.testutil.Suite; +import com.google.devtools.build.lib.testutil.TestSpec; +import com.google.devtools.build.lib.util.BlazeClock; +import com.google.devtools.build.lib.util.Clock; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * Unit tests for the profiler. + */ +@TestSpec(size = Suite.MEDIUM_TESTS) // testConcurrentProfiling takes ~700ms, testProfiler 100ms. +@RunWith(JUnit4.class) +public class ProfilerTest extends FoundationTestCase { + + private Path cacheDir; + private Profiler profiler = Profiler.instance(); + + @Before + public final void createCacheDirectory() throws Exception { + cacheDir = scratch.dir("/tmp"); + } + + @Test + public void testProfilerActivation() throws Exception { + Path cacheFile = cacheDir.getRelative("profile1.dat"); + assertFalse(profiler.isActive()); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + assertTrue(profiler.isActive()); + + profiler.stop(); + assertFalse(profiler.isActive()); + } + + @Test + public void testTaskDetails() throws Exception { + Path cacheFile = cacheDir.getRelative("profile1.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.startTask(ProfilerTask.ACTION, "action task"); + profiler.logEvent(ProfilerTask.TEST, "event"); + profiler.completeTask(ProfilerTask.ACTION); + profiler.stop(); + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + + ProfileInfo.Task task = info.allTasksById.get(0); + assertEquals(1, task.id); + assertEquals(ProfilerTask.ACTION, task.type); + assertEquals("action task", task.getDescription()); + + task = info.allTasksById.get(1); + assertEquals(2, task.id); + assertEquals(ProfilerTask.TEST, task.type); + assertEquals("event", task.getDescription()); + } + + @Test + public void testProfiler() throws Exception { + Path cacheFile = cacheDir.getRelative("profile1.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.logSimpleTask(getTestClock().nanoTime(), + ProfilerTask.PHASE, "profiler start"); + profiler.startTask(ProfilerTask.ACTION, "complex task"); + profiler.logEvent(ProfilerTask.PHASE, "event1"); + profiler.startTask(ProfilerTask.ACTION_CHECK, "complex subtask"); + // next task takes less than 10 ms and should be only aggregated + profiler.logSimpleTask(getTestClock().nanoTime(), + ProfilerTask.VFS_STAT, "stat1"); + long startTime = getTestClock().nanoTime(); + Thread.sleep(20); + // this one will take at least 20 ms and should be present + profiler.logSimpleTask(startTime, ProfilerTask.VFS_STAT, "stat2"); + profiler.completeTask(ProfilerTask.ACTION_CHECK); + profiler.completeTask(ProfilerTask.ACTION); + profiler.stop(); + // all other calls to profiler should be ignored + profiler.logEvent(ProfilerTask.PHASE, "should be ignored"); + // normally this would cause an exception but it is ignored since profiler + // is disabled + profiler.completeTask(ProfilerTask.ACTION_EXECUTE); + + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + assertThat(info.allTasksById).hasSize(6); // only 5 tasks + finalization should be recorded + + ProfileInfo.Task task = info.allTasksById.get(0); + assertTrue(task.stats.isEmpty()); + + task = info.allTasksById.get(1); + int count = 0; + for (ProfileInfo.AggregateAttr attr : task.getStatAttrArray()) { + if (attr != null) { + count++; + } + } + assertEquals(count, 2); // only children are GENERIC and ACTION_CHECK + assertEquals(task.aggregatedStats.toArray().length, ProfilerTask.TASK_COUNT); + assertEquals(task.aggregatedStats.getAttr(ProfilerTask.VFS_STAT).count, 2); + + task = info.allTasksById.get(2); + assertThat(task.durationNanos).isEqualTo(0); + + task = info.allTasksById.get(3); + assertEquals(task.stats.getAttr(ProfilerTask.VFS_STAT).count, 2); + assertEquals(task.subtasks.length, 1); + assertEquals(task.subtasks[0].getDescription(), "stat2"); + // assert that startTime grows with id + long time = -1; + for (ProfileInfo.Task t : info.allTasksById) { + assertTrue(t.startTime >= time); + time = t.startTime; + } + } + + @Test + public void testProfilerRecordingAllEvents() throws Exception { + Path cacheFile = cacheDir.getRelative("profile1.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", true, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.startTask(ProfilerTask.ACTION, "action task"); + // Next task takes less than 10 ms but should be recorded anyway. + profiler.logSimpleTask(getTestClock().nanoTime(), ProfilerTask.VFS_STAT, "stat1"); + profiler.completeTask(ProfilerTask.ACTION); + profiler.stop(); + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + assertThat(info.allTasksById).hasSize(3); // 2 tasks + finalization should be recorded + + ProfileInfo.Task task = info.allTasksById.get(1); + assertEquals(ProfilerTask.VFS_STAT, task.type); + + // Check that task would have been dropped if profiler was not configured to record everything. + assertThat(task.durationNanos).isLessThan(ProfilerTask.VFS_STAT.minDuration); + } + + @Test + public void testProfilerRecordingOnlySlowestEvents() throws Exception { + Path profileData = cacheDir.getRelative("foo"); + + profiler.start(ProfiledTaskKinds.SLOWEST, profileData.getOutputStream(), "test", true, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.logSimpleTask(10000, 20000, ProfilerTask.VFS_STAT, "stat"); + profiler.logSimpleTask(20000, 30000, ProfilerTask.REMOTE_EXECUTION, "remote execution"); + + assertTrue(profiler.isProfiling(ProfilerTask.VFS_STAT)); + assertFalse(profiler.isProfiling(ProfilerTask.REMOTE_EXECUTION)); + + profiler.stop(); + + ProfileInfo info = ProfileInfo.loadProfile(profileData); + info.calculateStats(); + assertThat(info.allTasksById).hasSize(1); // only VFS_STAT task should be recorded + + ProfileInfo.Task task = info.allTasksById.get(0); + assertEquals(ProfilerTask.VFS_STAT, task.type); + } + + @Test + public void testProfilerRecordsNothing() throws Exception { + Path profileData = cacheDir.getRelative("foo"); + + profiler.start(ProfiledTaskKinds.NONE, profileData.getOutputStream(), "test", true, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.logSimpleTask(10000, 20000, ProfilerTask.VFS_STAT, "stat"); + + assertTrue(ProfilerTask.VFS_STAT.collectsSlowestInstances()); + assertFalse(profiler.isProfiling(ProfilerTask.VFS_STAT)); + + profiler.stop(); + + ProfileInfo info = ProfileInfo.loadProfile(profileData); + info.calculateStats(); + assertThat(info.allTasksById).isEmpty(); + } + + @Test + public void testInconsistentCompleteTask() throws Exception { + Path cacheFile = cacheDir.getRelative("profile2.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), + "task stack inconsistency test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.startTask(ProfilerTask.PHASE, "some task"); + try { + profiler.completeTask(ProfilerTask.ACTION); + fail(); + } catch (IllegalStateException e) { + // this is expected + } + profiler.stop(); + } + + @Test + public void testConcurrentProfiling() throws Exception { + Path cacheFile = cacheDir.getRelative("profile3.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "concurrent test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + + long id = Thread.currentThread().getId(); + Thread thread1 = new Thread() { + @Override public void run() { + for (int i = 0; i < 10000; i++) { + Profiler.instance().logEvent(ProfilerTask.TEST, "thread1"); + } + } + }; + long id1 = thread1.getId(); + Thread thread2 = new Thread() { + @Override public void run() { + for (int i = 0; i < 10000; i++) { + Profiler.instance().logEvent(ProfilerTask.TEST, "thread2"); + } + } + }; + long id2 = thread2.getId(); + + profiler.startTask(ProfilerTask.PHASE, "main task"); + profiler.logEvent(ProfilerTask.TEST, "starting threads"); + thread1.start(); + thread2.start(); + thread2.join(); + thread1.join(); + profiler.logEvent(ProfilerTask.TEST, "joined"); + profiler.completeTask(ProfilerTask.PHASE); + profiler.stop(); + + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + info.analyzeRelationships(); + assertEquals(info.allTasksById.size(), 4 + 10000 + 10000); // total number of tasks + assertEquals(info.tasksByThread.size(), 3); // total number of threads + // while main thread had 3 tasks, 2 of them were nested, so tasksByThread + // would contain only one "main task" task + assertEquals(info.tasksByThread.get(id).length, 2); + ProfileInfo.Task mainTask = info.tasksByThread.get(id)[0]; + assertEquals(mainTask.getDescription(), "main task"); + assertEquals(mainTask.subtasks.length, 2); + // other threads had 10000 independent recorded tasks each + assertEquals(info.tasksByThread.get(id1).length, 10000); + assertEquals(info.tasksByThread.get(id2).length, 10000); + int startId = mainTask.subtasks[0].id; // id of "starting threads" + int endId = mainTask.subtasks[1].id; // id of "joining" + assertTrue(startId < info.tasksByThread.get(id1)[0].id); + assertTrue(startId < info.tasksByThread.get(id2)[0].id); + assertTrue(endId > info.tasksByThread.get(id1)[9999].id); + assertTrue(endId > info.tasksByThread.get(id2)[9999].id); + } + + @Test + public void testPhaseTasks() throws Exception { + Path cacheFile = cacheDir.getRelative("profile4.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "phase test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + Thread thread1 = new Thread() { + @Override public void run() { + for (int i = 0; i < 100; i++) { + Profiler.instance().logEvent(ProfilerTask.TEST, "thread1"); + } + } + }; + profiler.markPhase(ProfilePhase.INIT); // Empty phase. + profiler.markPhase(ProfilePhase.LOAD); + thread1.start(); + thread1.join(); + Thread.sleep(1); + profiler.markPhase(ProfilePhase.ANALYZE); + Thread thread2 = new Thread() { + @Override public void run() { + profiler.startTask(ProfilerTask.TEST, "complex task"); + for (int i = 0; i < 100; i++) { + Profiler.instance().logEvent(ProfilerTask.TEST, "thread2a"); + } + profiler.completeTask(ProfilerTask.TEST); + profiler.markPhase(ProfilePhase.EXECUTE); + for (int i = 0; i < 100; i++) { + Profiler.instance().logEvent(ProfilerTask.TEST, "thread2b"); + } + } + }; + thread2.start(); + thread2.join(); + profiler.logEvent(ProfilerTask.TEST, "last task"); + Thread.sleep(1); + profiler.stop(); + + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + info.analyzeRelationships(); + // number of tasks: INIT(1) + LOAD(1) + Thread1.TEST(100) + ANALYZE(1) + // + Thread2a.TEST(100) + TEST(1) + EXECUTE(1) + Thread2b.TEST(100) + TEST(1) + INFO(1) + assertThat(info.allTasksById).hasSize(1 + 1 + 100 + 1 + 100 + 1 + 1 + 100 + 1 + 1); + assertThat(info.tasksByThread).hasSize(3); // total number of threads + // Phase0 contains only itself + ProfileInfo.Task p0 = info.getPhaseTask(ProfilePhase.INIT); + assertThat(info.getTasksForPhase(p0)).hasSize(1); + // Phase1 contains itself and 100 TEST "thread1" tasks + ProfileInfo.Task p1 = info.getPhaseTask(ProfilePhase.LOAD); + assertThat(info.getTasksForPhase(p1)).hasSize(101); + // Phase2 contains itself and 1 "complex task" + ProfileInfo.Task p2 = info.getPhaseTask(ProfilePhase.ANALYZE); + assertThat(info.getTasksForPhase(p2)).hasSize(2); + // Phase3 contains itself, 100 TEST "thread2b" tasks and "last task" + ProfileInfo.Task p3 = info.getPhaseTask(ProfilePhase.EXECUTE); + assertThat(info.getTasksForPhase(p3)).hasSize(103); + } + + @Test + public void testCorruptedFile() throws Exception { + Path cacheFile = cacheDir.getRelative("profile5.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "phase test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + for (int i = 0; i < 100; i++) { + profiler.startTask(ProfilerTask.TEST, "outer task " + i); + profiler.logEvent(ProfilerTask.TEST, "inner task " + i); + profiler.completeTask(ProfilerTask.TEST); + } + profiler.stop(); + + ProfileInfo info = ProfileInfo.loadProfile(cacheFile); + info.calculateStats(); + assertFalse(info.isCorruptedOrIncomplete()); + + Path corruptedFile = cacheDir.getRelative("profile5bad.dat"); + FileSystemUtils.writeContent( + corruptedFile, Arrays.copyOf(FileSystemUtils.readContent(cacheFile), 2000)); + info = ProfileInfo.loadProfile(corruptedFile); + info.calculateStats(); + assertTrue(info.isCorruptedOrIncomplete()); + // Since root tasks will appear after nested tasks in the profile file and + // we have exactly one nested task for each root task, then following will be + // always true for our corrupted file: + // 0 <= number_of_all_tasks - 2*number_of_root_tasks <= 1 + assertEquals(info.rootTasksById.size(), info.allTasksById.size() / 2); + } + + @Test + public void testUnsupportedProfilerRecord() throws Exception { + Path dataFile = cacheDir.getRelative("profile5.dat"); + profiler.start(ProfiledTaskKinds.ALL, dataFile.getOutputStream(), "phase test", false, + BlazeClock.instance(), BlazeClock.instance().nanoTime()); + profiler.startTask(ProfilerTask.TEST, "outer task"); + profiler.logEvent(ProfilerTask.EXCEPTION, "inner task"); + profiler.completeTask(ProfilerTask.TEST); + profiler.startTask(ProfilerTask.SCANNER, "outer task 2"); + profiler.logSimpleTask(Profiler.nanoTimeMaybe(), ProfilerTask.TEST, "inner task 2"); + profiler.completeTask(ProfilerTask.SCANNER); + profiler.stop(); + + // Validate our test profile. + ProfileInfo info = ProfileInfo.loadProfile(dataFile); + info.calculateStats(); + assertFalse(info.isCorruptedOrIncomplete()); + assertEquals(2, info.getStatsForType(ProfilerTask.TEST, info.rootTasksById).count); + assertEquals(0, info.getStatsForType(ProfilerTask.UNKNOWN, info.rootTasksById).count); + + // Now replace "TEST" type with something unsupported - e.g. "XXXX". + InputStream in = new InflaterInputStream(dataFile.getInputStream(), new Inflater(false), 65536); + byte[] buffer = new byte[60000]; + int len = in.read(buffer); + in.close(); + assertTrue(len < buffer.length); // Validate that file was completely decoded. + String content = new String(buffer, ISO_8859_1); + int infoIndex = content.indexOf("TEST"); + assertTrue(infoIndex > 0); + content = content.substring(0, infoIndex) + "XXXX" + content.substring(infoIndex + 4); + OutputStream out = new DeflaterOutputStream(dataFile.getOutputStream(), + new Deflater(Deflater.BEST_SPEED, false), 65536); + out.write(content.getBytes(ISO_8859_1)); + out.close(); + + // Validate that XXXX records were classified as UNKNOWN. + info = ProfileInfo.loadProfile(dataFile); + info.calculateStats(); + assertFalse(info.isCorruptedOrIncomplete()); + assertEquals(0, info.getStatsForType(ProfilerTask.TEST, info.rootTasksById).count); + assertEquals(1, info.getStatsForType(ProfilerTask.SCANNER, info.rootTasksById).count); + assertEquals(1, info.getStatsForType(ProfilerTask.EXCEPTION, info.rootTasksById).count); + assertEquals(2, info.getStatsForType(ProfilerTask.UNKNOWN, info.rootTasksById).count); + } + + @Test + public void testResilenceToNonDecreasingNanoTimes() throws Exception { + final long initialNanoTime = BlazeClock.instance().nanoTime(); + final AtomicInteger numNanoTimeCalls = new AtomicInteger(0); + Clock badClock = new Clock() { + @Override + public long currentTimeMillis() { + return BlazeClock.instance().currentTimeMillis(); + } + + @Override + public long nanoTime() { + return initialNanoTime - numNanoTimeCalls.addAndGet(1); + } + }; + Path cacheFile = cacheDir.getRelative("profile1.dat"); + profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), + "testResilenceToNonDecreasingNanoTimes", false, badClock, initialNanoTime); + profiler.logSimpleTask(badClock.nanoTime(), ProfilerTask.TEST, "some task"); + profiler.stop(); + } + + private Clock getTestClock() { + return BlazeClock.instance(); + } + +} |