aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test
diff options
context:
space:
mode:
authorGravatar Florian Weikert <fwe@google.com>2015-12-07 15:02:00 +0000
committerGravatar David Chen <dzc@google.com>2015-12-07 21:19:32 +0000
commit2be2f385c61ec50e356bd98069ad037ad66f128a (patch)
tree7daaee06daf6a8eecca257b5af240fc1375284c0 /src/test
parent6c440f20a8a300d63d33f01c66595a9640d57099 (diff)
Open-sourced tests from lib/profiler.
-- MOS_MIGRATED_REVID=109577273
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/com/google/devtools/build/lib/BUILD22
-rw-r--r--src/test/java/com/google/devtools/build/lib/profiler/AutoProfilerTest.java53
-rw-r--r--src/test/java/com/google/devtools/build/lib/profiler/ProfilerChartTest.java306
-rw-r--r--src/test/java/com/google/devtools/build/lib/profiler/ProfilerTest.java446
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();
+ }
+
+}