1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/dynamic/experimental_slice_layout_generator.h"
18
19 #include <algorithm>
20
21 #include "test/gtest_and_gmock.h"
22
23 namespace perfetto {
24 namespace trace_processor {
25 namespace {
26
27 constexpr uint32_t kColumn =
28 ExperimentalSliceLayoutGenerator::kFilterTrackIdsColumnIndex;
29
ToVis(const Table & table)30 std::string ToVis(const Table& table) {
31 const Column* layout_depth_column = table.GetColumnByName("layout_depth");
32 const Column* ts_column = table.GetColumnByName("ts");
33 const Column* dur_column = table.GetColumnByName("dur");
34 const Column* filter_track_ids_column =
35 table.GetColumnByName("filter_track_ids");
36
37 std::vector<std::string> lines;
38 for (uint32_t i = 0; i < table.row_count(); ++i) {
39 int64_t layout_depth = layout_depth_column->Get(i).long_value;
40 int64_t ts = ts_column->Get(i).long_value;
41 int64_t dur = dur_column->Get(i).long_value;
42 const char* filter_track_ids = filter_track_ids_column->Get(i).AsString();
43 if (std::string("") == filter_track_ids) {
44 continue;
45 }
46 for (int64_t j = 0; j < dur; ++j) {
47 size_t y = static_cast<size_t>(layout_depth);
48 size_t x = static_cast<size_t>(ts + j);
49 while (lines.size() <= y) {
50 lines.push_back("");
51 }
52 if (lines[y].size() <= x) {
53 lines[y].resize(x + 1, ' ');
54 }
55 lines[y][x] = '#';
56 }
57 }
58
59 std::string output = "";
60 output += "\n";
61 for (const std::string& line : lines) {
62 output += line;
63 output += "\n";
64 }
65 return output;
66 }
67
ExpectOutput(const Table & table,const std::string & expected)68 void ExpectOutput(const Table& table, const std::string& expected) {
69 const auto& actual = ToVis(table);
70 EXPECT_EQ(actual, expected)
71 << "Actual:" << actual << "\nExpected:" << expected;
72 }
73
Insert(tables::SliceTable * table,int64_t ts,int64_t dur,uint32_t track_id,StringId name,base::Optional<tables::SliceTable::Id> parent_id)74 tables::SliceTable::Id Insert(
75 tables::SliceTable* table,
76 int64_t ts,
77 int64_t dur,
78 uint32_t track_id,
79 StringId name,
80 base::Optional<tables::SliceTable::Id> parent_id) {
81 tables::SliceTable::Row row;
82 row.ts = ts;
83 row.dur = dur;
84 row.depth = 0;
85 base::Optional<tables::SliceTable::Id> id = parent_id;
86 while (id) {
87 row.depth++;
88 id = table->parent_id()[id.value().value];
89 }
90 row.track_id = tables::TrackTable::Id{track_id};
91 row.name = name;
92 row.parent_id = parent_id;
93 return table->Insert(row).id;
94 }
95
TEST(ExperimentalSliceLayoutGeneratorTest,SingleRow)96 TEST(ExperimentalSliceLayoutGeneratorTest, SingleRow) {
97 StringPool pool;
98 tables::SliceTable slice_table(&pool, nullptr);
99 StringId name = pool.InternString("SingleRow");
100
101 Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
102 base::nullopt /*parent*/);
103
104 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
105
106 std::unique_ptr<Table> table = gen.ComputeTable(
107 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
108 ExpectOutput(*table, R"(
109 #####
110 )");
111 }
112
TEST(ExperimentalSliceLayoutGeneratorTest,DoubleRow)113 TEST(ExperimentalSliceLayoutGeneratorTest, DoubleRow) {
114 StringPool pool;
115 tables::SliceTable slice_table(&pool, nullptr);
116 StringId name = pool.InternString("SingleRow");
117
118 auto id = Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
119 base::nullopt);
120 Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name, id);
121
122 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
123
124 std::unique_ptr<Table> table = gen.ComputeTable(
125 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
126 ExpectOutput(*table, R"(
127 #####
128 #####
129 )");
130 }
131
TEST(ExperimentalSliceLayoutGeneratorTest,MultipleRows)132 TEST(ExperimentalSliceLayoutGeneratorTest, MultipleRows) {
133 StringPool pool;
134 tables::SliceTable slice_table(&pool, nullptr);
135 StringId name = pool.InternString("MultipleRows");
136
137 auto a = Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
138 base::nullopt);
139 auto b = Insert(&slice_table, 1 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name, a);
140 auto c = Insert(&slice_table, 1 /*ts*/, 3 /*dur*/, 1 /*track_id*/, name, b);
141 auto d = Insert(&slice_table, 1 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name, c);
142 auto e = Insert(&slice_table, 1 /*ts*/, 1 /*dur*/, 1 /*track_id*/, name, d);
143 base::ignore_result(e);
144
145 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
146
147 std::unique_ptr<Table> table = gen.ComputeTable(
148 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1")}}, {});
149 ExpectOutput(*table, R"(
150 #####
151 ####
152 ###
153 ##
154 #
155 )");
156 }
157
TEST(ExperimentalSliceLayoutGeneratorTest,MultipleTracks)158 TEST(ExperimentalSliceLayoutGeneratorTest, MultipleTracks) {
159 StringPool pool;
160 tables::SliceTable slice_table(&pool, nullptr);
161 StringId name1 = pool.InternString("Slice1");
162 StringId name2 = pool.InternString("Slice2");
163 StringId name3 = pool.InternString("Slice3");
164 StringId name4 = pool.InternString("Track4");
165
166 auto a = Insert(&slice_table, 1 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
167 base::nullopt);
168 auto b = Insert(&slice_table, 1 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
169 auto x = Insert(&slice_table, 4 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
170 base::nullopt);
171 auto y = Insert(&slice_table, 4 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, x);
172 base::ignore_result(b);
173 base::ignore_result(y);
174
175 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
176
177 std::unique_ptr<Table> table = gen.ComputeTable(
178 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
179 ExpectOutput(*table, R"(
180 ####
181 ##
182 ####
183 ##
184 )");
185 }
186
TEST(ExperimentalSliceLayoutGeneratorTest,MultipleTracksWithGap)187 TEST(ExperimentalSliceLayoutGeneratorTest, MultipleTracksWithGap) {
188 StringPool pool;
189 tables::SliceTable slice_table(&pool, nullptr);
190 StringId name1 = pool.InternString("Slice1");
191 StringId name2 = pool.InternString("Slice2");
192 StringId name3 = pool.InternString("Slice3");
193 StringId name4 = pool.InternString("Slice4");
194 StringId name5 = pool.InternString("Slice5");
195 StringId name6 = pool.InternString("Slice6");
196
197 auto a = Insert(&slice_table, 0 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
198 base::nullopt);
199 auto b = Insert(&slice_table, 0 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
200 auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
201 base::nullopt);
202 auto q = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, p);
203 auto x = Insert(&slice_table, 5 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name5,
204 base::nullopt);
205 auto y = Insert(&slice_table, 5 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name6, x);
206 base::ignore_result(b);
207 base::ignore_result(q);
208 base::ignore_result(y);
209
210 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
211
212 std::unique_ptr<Table> table = gen.ComputeTable(
213 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
214 ExpectOutput(*table, R"(
215 #### ####
216 ## ##
217 ####
218 ##
219 )");
220 }
221
TEST(ExperimentalSliceLayoutGeneratorTest,FilterOutTracks)222 TEST(ExperimentalSliceLayoutGeneratorTest, FilterOutTracks) {
223 StringPool pool;
224 tables::SliceTable slice_table(&pool, nullptr);
225 StringId name1 = pool.InternString("Slice1");
226 StringId name2 = pool.InternString("Slice2");
227 StringId name3 = pool.InternString("Slice3");
228 StringId name4 = pool.InternString("Slice4");
229 StringId name5 = pool.InternString("Slice5");
230
231 auto a = Insert(&slice_table, 0 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
232 base::nullopt);
233 auto b = Insert(&slice_table, 0 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
234 auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
235 base::nullopt);
236 auto q = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, p);
237 // This slice should be ignored as it's not in the filter below:
238 Insert(&slice_table, 0 /*ts*/, 9 /*dur*/, 3 /*track_id*/, name5,
239 base::nullopt);
240 base::ignore_result(b);
241 base::ignore_result(q);
242
243 ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
244 std::unique_ptr<Table> table = gen.ComputeTable(
245 {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2")}}, {});
246 ExpectOutput(*table, R"(
247 ####
248 ##
249 ####
250 ##
251 )");
252 }
253
254 } // namespace
255 } // namespace trace_processor
256 } // namespace perfetto
257