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