Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 1 | // Copyright 2015 The Bazel Authors. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | package com.google.devtools.build.lib.profiler; |
| 15 | |
| 16 | import static com.google.common.truth.Truth.assertThat; |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 17 | |
| 18 | import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds; |
| 19 | import com.google.devtools.build.lib.profiler.chart.AggregatingChartCreator; |
| 20 | import com.google.devtools.build.lib.profiler.chart.Chart; |
| 21 | import com.google.devtools.build.lib.profiler.chart.ChartBar; |
| 22 | import com.google.devtools.build.lib.profiler.chart.ChartBarType; |
| 23 | import com.google.devtools.build.lib.profiler.chart.ChartColumn; |
| 24 | import com.google.devtools.build.lib.profiler.chart.ChartCreator; |
| 25 | import com.google.devtools.build.lib.profiler.chart.ChartLine; |
| 26 | import com.google.devtools.build.lib.profiler.chart.ChartRow; |
| 27 | import com.google.devtools.build.lib.profiler.chart.ChartVisitor; |
| 28 | import com.google.devtools.build.lib.profiler.chart.Color; |
| 29 | import com.google.devtools.build.lib.profiler.chart.DetailedChartCreator; |
| 30 | import com.google.devtools.build.lib.testutil.FoundationTestCase; |
| 31 | import com.google.devtools.build.lib.testutil.Scratch; |
| 32 | import com.google.devtools.build.lib.testutil.Suite; |
| 33 | import com.google.devtools.build.lib.testutil.TestSpec; |
| 34 | import com.google.devtools.build.lib.util.BlazeClock; |
| 35 | import com.google.devtools.build.lib.vfs.Path; |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 36 | import java.util.List; |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 37 | import org.junit.Test; |
| 38 | import org.junit.runner.RunWith; |
| 39 | import org.junit.runners.JUnit4; |
| 40 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 41 | /** Unit tests for the profiler chart generation. */ |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 42 | @TestSpec(size = Suite.MEDIUM_TESTS) |
| 43 | @RunWith(JUnit4.class) |
| 44 | public class ProfilerChartTest extends FoundationTestCase { |
| 45 | private static final int COMMON_CHART_TYPES = ProfilePhase.values().length; |
| 46 | private static final int DETAILED_CHART_TYPES = ProfilerTask.values().length; |
| 47 | private static final int AGGREGATED_CHART_TYPES = 4; |
| 48 | private static final int AGGREGATED_CHART_NO_VFS_TYPES = 3; |
| 49 | |
| 50 | @Test |
| 51 | public void testChartCreators() throws Exception { |
| 52 | Runnable run = new Runnable() { |
| 53 | @Override |
| 54 | public void run() { |
| 55 | Profiler.instance().startTask(ProfilerTask.ACTION, "action"); |
| 56 | Profiler.instance().completeTask(ProfilerTask.ACTION); |
| 57 | } |
| 58 | }; |
| 59 | int threads = 4; // there is one extra thread due due the event that finalizes the profiler |
| 60 | ProfileInfo info = createProfileInfo(run, threads - 1); |
| 61 | ChartCreator aggregatingCreator = new AggregatingChartCreator(info, true); |
| 62 | Chart aggregatedChart = aggregatingCreator.create(); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 63 | assertThat(aggregatedChart.getRowCount()).isEqualTo(threads); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 64 | assertThat(aggregatedChart.getSortedRows().get(0).getBars()).hasSize(1); |
| 65 | |
| 66 | ChartCreator detailedCreator = new DetailedChartCreator(info); |
| 67 | Chart detailedChart = detailedCreator.create(); |
| 68 | assertThat(detailedChart.getSortedTypes()).hasSize(COMMON_CHART_TYPES + DETAILED_CHART_TYPES); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 69 | assertThat(detailedChart.getRowCount()).isEqualTo(threads); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 70 | assertThat(detailedChart.getSortedRows().get(0).getBars()).hasSize(1); |
| 71 | } |
| 72 | |
| 73 | @Test |
| 74 | public void testAggregatingChartCreator() throws Exception { |
| 75 | Runnable run = new Runnable() { |
| 76 | @Override |
| 77 | public void run() { |
| 78 | Profiler profiler = Profiler.instance(); |
| 79 | profiler.startTask(ProfilerTask.ACTION, "action"); // Stays |
| 80 | task(profiler, ProfilerTask.REMOTE_EXECUTION, "remote execution"); // Removed |
| 81 | task(profiler, ProfilerTask.ACTION_CHECK, "check"); // Removed |
| 82 | task(profiler, ProfilerTask.ACTION_LOCK, "lock"); // Stays |
| 83 | profiler.completeTask(ProfilerTask.ACTION); |
| 84 | task(profiler, ProfilerTask.INFO, "info"); // Stays |
| 85 | task(profiler, ProfilerTask.VFS_STAT, "stat"); // Stays, if showVFS |
| 86 | task(profiler, ProfilerTask.WAIT, "wait"); // Stays |
| 87 | } |
| 88 | }; |
| 89 | ProfileInfo info = createProfileInfo(run, 1); |
| 90 | |
| 91 | ChartCreator aggregatingCreator = new AggregatingChartCreator(info, true); |
| 92 | Chart aggregatedChart = aggregatingCreator.create(); |
| 93 | assertThat(aggregatedChart.getSortedTypes()) |
| 94 | .hasSize(COMMON_CHART_TYPES + AGGREGATED_CHART_TYPES); |
| 95 | assertThat(aggregatedChart.getSortedRows().get(0).getBars()).hasSize(5); |
| 96 | |
| 97 | ChartCreator aggregatingNoVfsCreator = new AggregatingChartCreator(info, false); |
| 98 | Chart aggregatedNoVfsChart = aggregatingNoVfsCreator.create(); |
| 99 | assertThat(aggregatedNoVfsChart.getSortedTypes()) |
| 100 | .hasSize(COMMON_CHART_TYPES + AGGREGATED_CHART_NO_VFS_TYPES); |
| 101 | assertThat(aggregatedNoVfsChart.getSortedRows().get(0).getBars()).hasSize(4); |
| 102 | |
| 103 | ChartCreator detailedCreator = new DetailedChartCreator(info); |
| 104 | Chart detailedChart = detailedCreator.create(); |
| 105 | assertThat(detailedChart.getSortedTypes()) |
| 106 | .hasSize(COMMON_CHART_TYPES + ProfilerTask.values().length); |
| 107 | assertThat(detailedChart.getSortedRows().get(0).getBars()).hasSize(7); |
| 108 | } |
| 109 | |
| 110 | @Test |
| 111 | public void testChart() throws Exception { |
| 112 | Chart chart = new Chart(); |
| 113 | |
| 114 | ChartBarType type3 = chart.createType("name3", Color.GREEN); |
| 115 | ChartBarType type2 = chart.createType("name2", Color.RED); |
| 116 | ChartBarType type1 = chart.createType("name1", Color.BLACK); |
| 117 | List<ChartBarType> types = chart.getSortedTypes(); |
| 118 | assertThat(types).hasSize(3); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 119 | assertThat(types.get(0).getName()).isEqualTo(type1.getName()); |
| 120 | assertThat(types.get(0).getColor()).isEqualTo(type1.getColor()); |
| 121 | assertThat(types.get(1).getName()).isEqualTo(type2.getName()); |
| 122 | assertThat(types.get(1).getColor()).isEqualTo(type2.getColor()); |
| 123 | assertThat(types.get(2).getName()).isEqualTo(type3.getName()); |
| 124 | assertThat(types.get(2).getColor()).isEqualTo(type3.getColor()); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 125 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 126 | assertThat(chart.lookUpType("name3")).isSameAs(type3); |
| 127 | assertThat(chart.lookUpType("name2")).isSameAs(type2); |
| 128 | assertThat(chart.lookUpType("name1")).isSameAs(type1); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 129 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 130 | assertThat(chart.lookUpType("wergl")).isSameAs(Chart.UNKNOWN_TYPE); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 131 | types = chart.getSortedTypes(); |
| 132 | assertThat(types).hasSize(4); |
| 133 | |
| 134 | chart.addBar(1, 2, 3, type1, "label1"); |
| 135 | chart.addBar(2, 3, 4, type2, "label2"); |
| 136 | chart.addBar(2, 4, 5, type3, "label3"); |
| 137 | chart.addBar(3, 3, 4, type2, "label4"); |
| 138 | chart.addBar(3, 4, 5, type3, "label5"); |
| 139 | chart.addBar(3, 5, 6, type3, "label6"); |
| 140 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 141 | assertThat(chart.getMaxStop()).isEqualTo(6); |
| 142 | assertThat(chart.getRowCount()).isEqualTo(3); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 143 | |
| 144 | List<ChartRow> rows = chart.getSortedRows(); |
| 145 | assertThat(rows).hasSize(3); |
| 146 | assertThat(rows.get(0).getBars()).hasSize(1); |
| 147 | assertThat(rows.get(1).getBars()).hasSize(2); |
| 148 | assertThat(rows.get(2).getBars()).hasSize(3); |
| 149 | |
| 150 | ChartBar bar = rows.get(0).getBars().get(0); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 151 | assertThat(bar.getStart()).isEqualTo(2); |
| 152 | assertThat(bar.getStop()).isEqualTo(3); |
| 153 | assertThat(bar.getType()).isSameAs(type1); |
| 154 | assertThat(bar.getLabel()).isEqualTo("label1"); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | @Test |
| 158 | public void testChartRows() throws Exception { |
| 159 | ChartRow row1 = new ChartRow("1", 0); |
| 160 | ChartRow row2 = new ChartRow("2", 1); |
| 161 | ChartRow row3 = new ChartRow("3", 1); |
| 162 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 163 | assertThat(row1.getId()).isEqualTo("1"); |
| 164 | assertThat(row1.getIndex()).isEqualTo(0); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 165 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 166 | assertThat(row1.compareTo(row2)).isEqualTo(-1); |
| 167 | assertThat(row2.compareTo(row1)).isEqualTo(1); |
| 168 | assertThat(row2.compareTo(row3)).isEqualTo(0); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 169 | |
| 170 | row1.addBar(new ChartBar(row1, 1, 2, new ChartBarType("name1", Color.BLACK), false, "label1")); |
| 171 | row1.addBar(new ChartBar(row1, 2, 3, new ChartBarType("name2", Color.RED), false, "label2")); |
| 172 | |
| 173 | assertThat(row1.getBars()).hasSize(2); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 174 | assertThat(row1.getBars().get(0).getLabel()).isEqualTo("label1"); |
| 175 | assertThat(row1.getBars().get(1).getLabel()).isEqualTo("label2"); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | @Test |
| 179 | public void testChartBarTypes() throws Exception { |
| 180 | ChartBarType type1 = new ChartBarType("name1", Color.BLACK); |
| 181 | ChartBarType type2 = new ChartBarType("name2", Color.RED); |
| 182 | ChartBarType type3 = new ChartBarType("name2", Color.GREEN); |
| 183 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 184 | assertThat(type1.compareTo(type2)).isEqualTo(-1); |
| 185 | assertThat(type2.compareTo(type1)).isEqualTo(1); |
| 186 | assertThat(type2.compareTo(type3)).isEqualTo(0); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 187 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 188 | assertThat(type2).isEqualTo(type3); |
| 189 | assertThat(type1.equals(type3)).isFalse(); |
| 190 | assertThat(type1.equals(type2)).isFalse(); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 191 | |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 192 | assertThat(type2.hashCode()).isEqualTo(type3.hashCode()); |
| 193 | assertThat(type1.hashCode() == type2.hashCode()).isFalse(); |
| 194 | assertThat(type1.hashCode() == type3.hashCode()).isFalse(); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | @Test |
| 198 | public void testChartBar() throws Exception { |
| 199 | ChartRow row1 = new ChartRow("1", 0); |
| 200 | ChartBarType type = new ChartBarType("name1", Color.BLACK); |
| 201 | ChartBar bar1 = new ChartBar(row1, 1, 2, type, false, "label1"); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 202 | assertThat(bar1.getRow()).isEqualTo(row1); |
| 203 | assertThat(bar1.getStart()).isEqualTo(1); |
| 204 | assertThat(bar1.getStop()).isEqualTo(2); |
| 205 | assertThat(bar1.getType()).isSameAs(type); |
| 206 | assertThat(bar1.getLabel()).isEqualTo("label1"); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | @Test |
| 210 | public void testVisitor() throws Exception { |
| 211 | Chart chart = new Chart(); |
| 212 | ChartBarType type3 = chart.createType("name3", Color.GREEN); |
| 213 | ChartBarType type2 = chart.createType("name2", Color.RED); |
| 214 | ChartBarType type1 = chart.createType("name1", Color.BLACK); |
| 215 | chart.addBar(1, 2, 3, type1, "label1"); |
| 216 | chart.addBar(2, 3, 4, type2, "label2"); |
| 217 | chart.addBar(2, 4, 5, type3, "label3"); |
| 218 | chart.addBar(3, 3, 4, type2, "label4"); |
| 219 | chart.addBar(3, 4, 5, type3, "label5"); |
| 220 | chart.addBar(3, 5, 6, type3, "label6"); |
| 221 | |
| 222 | TestingChartVisitor visitor = new TestingChartVisitor(); |
| 223 | chart.accept(visitor); |
lberki | bf6ef0f | 2017-05-29 11:00:40 +0200 | [diff] [blame^] | 224 | assertThat(visitor.beginChartCount).isEqualTo(1); |
| 225 | assertThat(visitor.endChartCount).isEqualTo(1); |
| 226 | assertThat(visitor.rowCount).isEqualTo(3); |
| 227 | assertThat(visitor.barCount).isEqualTo(6); |
| 228 | assertThat(visitor.columnCount).isEqualTo(0); |
| 229 | assertThat(visitor.lineCount).isEqualTo(0); |
Florian Weikert | 2be2f38 | 2015-12-07 15:02:00 +0000 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | private ProfileInfo createProfileInfo(Runnable runnable, int noOfRows) throws Exception { |
| 233 | Scratch scratch = new Scratch(); |
| 234 | Path cacheDir = scratch.dir("/tmp"); |
| 235 | Path cacheFile = cacheDir.getRelative("profile1.dat"); |
| 236 | Profiler profiler = Profiler.instance(); |
| 237 | profiler.start(ProfiledTaskKinds.ALL, cacheFile.getOutputStream(), "basic test", false, |
| 238 | BlazeClock.instance(), BlazeClock.instance().nanoTime()); |
| 239 | |
| 240 | // Write from multiple threads to generate multiple rows in the chart. |
| 241 | for (int i = 0; i < noOfRows; i++) { |
| 242 | Thread t = new Thread(runnable); |
| 243 | t.start(); |
| 244 | t.join(); |
| 245 | } |
| 246 | |
| 247 | profiler.stop(); |
| 248 | return ProfileInfo.loadProfile(cacheFile); |
| 249 | } |
| 250 | |
| 251 | private void task(final Profiler profiler, ProfilerTask task, String name) { |
| 252 | profiler.startTask(task, name); |
| 253 | try { |
| 254 | Thread.sleep(100); |
| 255 | } catch (InterruptedException e) { |
| 256 | // ignore |
| 257 | } |
| 258 | profiler.completeTask(task); |
| 259 | } |
| 260 | |
| 261 | private static final class TestingChartVisitor implements ChartVisitor { |
| 262 | private int rowCount; |
| 263 | private int endChartCount; |
| 264 | private int barCount; |
| 265 | private int beginChartCount; |
| 266 | private int columnCount; |
| 267 | private int lineCount; |
| 268 | |
| 269 | @Override |
| 270 | public void visit(Chart chart) { |
| 271 | beginChartCount++; |
| 272 | } |
| 273 | |
| 274 | @Override |
| 275 | public void endVisit(Chart chart) { |
| 276 | endChartCount++; |
| 277 | } |
| 278 | |
| 279 | @Override |
| 280 | public void visit(ChartRow chartRow) { |
| 281 | rowCount++; |
| 282 | } |
| 283 | |
| 284 | @Override |
| 285 | public void visit(ChartBar chartBar) { |
| 286 | barCount++; |
| 287 | } |
| 288 | |
| 289 | @Override |
| 290 | public void visit(ChartColumn chartColumn) { |
| 291 | columnCount++; |
| 292 | } |
| 293 | |
| 294 | @Override |
| 295 | public void visit(ChartLine chartLine) { |
| 296 | lineCount++; |
| 297 | } |
| 298 | } |
| 299 | } |