| // 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(); | 
 |   } | 
 |  | 
 | } |