blob: 221753aaec3b577c99b1f57e9896714eecced38c [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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.
*/
#include "src/trace_processor/db/view.h"
#include "src/trace_processor/db/view_unittest_py.h"
#include "src/trace_processor/views/macros.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace trace_processor {
namespace tables {
ViewThreadTable::~ViewThreadTable() = default;
ViewTrackTable::~ViewTrackTable() = default;
ViewThreadTrackTable::~ViewThreadTrackTable() = default;
ViewEventTable::~ViewEventTable() = default;
ViewSliceTable::~ViewSliceTable() = default;
namespace {
template <typename ViewSubclass>
class AbstractViewTest : public ::testing::Test {
protected:
using ColIdx = typename ViewSubclass::ColumnIndex;
using QueryResult = typename ViewSubclass::QueryResult;
virtual ~AbstractViewTest() = default;
QueryResult Query(const std::vector<Constraint>& cs = {},
const std::vector<Order>& ob = {}) {
return Query(cs, ob, AllColsUsed(view()));
}
QueryResult QueryUsingCols(const std::vector<uint32_t>& cols_used) {
return Query({}, {}, cols_used);
}
QueryResult Query(const std::vector<Constraint>& cs,
const std::vector<Order>& ob,
const std::vector<uint32_t>& cols_used) {
return view().Query(cs, ob, IvToBv(view(), cols_used));
}
StringPool::Id Intern(const char* ptr) { return pool_.InternString(ptr); }
virtual ViewSubclass& view() = 0;
StringPool pool_;
private:
std::vector<uint32_t> AllColsUsed(const View& v) {
std::vector<uint32_t> used(v.GetColumnCount());
std::iota(used.begin(), used.end(), 0);
return used;
}
BitVector IvToBv(const View& v, const std::vector<uint32_t>& cols_used) {
BitVector bv(v.GetColumnCount());
for (uint32_t col : cols_used) {
bv.Set(col);
}
return bv;
}
};
#define PERFETTO_TP_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _) \
NAME(ViewEventView, "event_view") \
FROM(ViewEventTable, event) \
JOIN(ViewTrackTable, track, id, event, track_id, View::kIdAlwaysPresent) \
COL(id, event, id) \
COL(ts, event, ts) \
COL(track_id, event, track_id) \
COL(track_name, track, name)
PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_EVENT_VIEW_DEF);
PERFETTO_TP_DEFINE_VIEW(ViewEventView);
class EventViewTest : public AbstractViewTest<ViewEventView> {
protected:
EventViewTest() {
t1_id_ = track_.Insert({/* name */ Intern("foo")}).id;
t2_id_ = track_.Insert({/* name */ Intern("bar")}).id;
event_table_.Insert({/* ts */ 100, t1_id_});
event_table_.Insert({/* ts */ 101, t2_id_});
event_table_.Insert({/* ts */ 102, t1_id_});
}
virtual ViewEventView& view() override { return event_view_; }
ViewTrackTable::Id t1_id_;
ViewTrackTable::Id t2_id_;
private:
ViewEventTable event_table_{&pool_};
ViewTrackTable track_{&pool_};
ViewEventView event_view_{&event_table_, &track_};
};
TEST_F(EventViewTest, UnusedColumnsAreDummy) {
ViewEventView::QueryResult result = QueryUsingCols({ColIdx::track_name});
ASSERT_TRUE(result.columns()[ColIdx::id].IsDummy());
ASSERT_TRUE(result.columns()[ColIdx::ts].IsDummy());
ASSERT_FALSE(result.columns()[ColIdx::track_name].IsDummy());
}
TEST_F(EventViewTest, Iterate) {
ViewEventView::QueryResult result = Query();
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.row_number().row_number(), 0u);
ASSERT_EQ(it.ts(), 100);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.row_number().row_number(), 1u);
ASSERT_EQ(it.ts(), 101);
ASSERT_EQ(it.track_name(), Intern("bar"));
ASSERT_EQ(it.track_id(), t2_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.row_number().row_number(), 2u);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_FALSE(++it);
}
TEST_F(EventViewTest, FilterEventEmpty) {
ViewEventView::QueryResult result = Query({view().ts().eq(0)});
auto it = result.IterateRows();
ASSERT_FALSE(it);
}
TEST_F(EventViewTest, FilterEventNoUseTrack) {
ViewEventView::QueryResult result =
Query({view().ts().eq(100)}, {}, {ColIdx::ts});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 100);
ASSERT_FALSE(++it);
}
TEST_F(EventViewTest, FilterEventUseTrack) {
ViewEventView::QueryResult result =
Query({view().ts().eq(100)}, {},
{ColIdx::ts, ColIdx::track_name, ColIdx::track_id});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 100);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_FALSE(++it);
}
TEST_F(EventViewTest, FilterTrackEmpty) {
ViewEventView::QueryResult result = Query({view().track_id().eq(102398)});
auto it = result.IterateRows();
ASSERT_FALSE(it);
}
TEST_F(EventViewTest, FilterTrackNoUseEvent) {
ViewEventView::QueryResult result =
Query({view().track_name().eq("foo")}, {},
{ColIdx::track_name, ColIdx::track_id});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_FALSE(++it);
}
TEST_F(EventViewTest, FilterTrackUseEvent) {
ViewEventView::QueryResult result =
Query({view().track_id().eq(t1_id_.value)}, {},
{ColIdx::ts, ColIdx::track_name, ColIdx::track_id});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 100);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_name(), Intern("foo"));
ASSERT_EQ(it.track_id(), t1_id_);
ASSERT_FALSE(++it);
}
#define PERFETTO_TP_THREAD_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _) \
NAME(ViewThreadEventView, "thread_event_view") \
FROM(ViewEventTable, event) \
JOIN(ViewThreadTrackTable, track, id, event, track_id, View::kNoFlag) \
JOIN(ViewThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) \
COL(id, event, id) \
COL(ts, event, ts) \
COL(track_id, track, id) \
COL(track_name, track, name) \
COL(utid, track, utid) \
COL(thread_name, thread, name)
PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_EVENT_VIEW_DEF);
PERFETTO_TP_DEFINE_VIEW(ViewThreadEventView);
class ThreadEventViewTest : public AbstractViewTest<ViewThreadEventView> {
protected:
ThreadEventViewTest() {
th1_id_ = thread_.Insert({Intern("th1"), 1}).id;
th2_id_ = thread_.Insert({Intern("th2"), 2}).id;
t1_id_ = track_.Insert({/* name */ Intern("t1")}).id;
t2_id_ = track_.Insert({/* name */ Intern("t2")}).id;
t3_id_ = thread_track_.Insert({/* name */ Intern("t3"), th2_id_}).id;
t4_id_ = thread_track_.Insert({/* name */ Intern("t4"), th1_id_}).id;
t5_id_ = thread_track_.Insert({/* name */ Intern("t5"), th2_id_}).id;
t6_id_ = track_.Insert({/* name */ Intern("t6")}).id;
event_table_.Insert({/* ts */ 100, t1_id_});
event_table_.Insert({/* ts */ 101, t2_id_});
event_table_.Insert({/* ts */ 102, t3_id_});
event_table_.Insert({/* ts */ 103, t5_id_});
event_table_.Insert({/* ts */ 104, t4_id_});
event_table_.Insert({/* ts */ 105, t5_id_});
event_table_.Insert({/* ts */ 106, t1_id_});
event_table_.Insert({/* ts */ 107, t4_id_});
}
virtual ViewThreadEventView& view() override { return event_view_; }
ViewThreadTable::Id th1_id_;
ViewThreadTable::Id th2_id_;
ViewTrackTable::Id t1_id_;
ViewTrackTable::Id t2_id_;
ViewTrackTable::Id t3_id_;
ViewTrackTable::Id t4_id_;
ViewTrackTable::Id t5_id_;
ViewTrackTable::Id t6_id_;
private:
ViewEventTable event_table_{&pool_};
ViewTrackTable track_{&pool_};
ViewThreadTrackTable thread_track_{&pool_, &track_};
ViewThreadTable thread_{&pool_};
ViewThreadEventView event_view_{&event_table_, &thread_track_, &thread_};
};
TEST_F(ThreadEventViewTest, Iterate) {
auto result = Query();
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_EQ(it.track_name(), Intern("t3"));
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 103);
ASSERT_EQ(it.track_name(), Intern("t5"));
ASSERT_EQ(it.track_id(), t5_id_);
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.track_name(), Intern("t4"));
ASSERT_EQ(it.utid(), th1_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 105);
ASSERT_EQ(it.track_id(), t5_id_);
ASSERT_EQ(it.track_name(), Intern("t5"));
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.track_name(), Intern("t4"));
ASSERT_EQ(it.utid(), th1_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadEventViewTest, FilterEventUseTrackAndThread) {
auto result =
Query({view().ts().ge(105)}, {},
{ColIdx::ts, ColIdx::track_id, ColIdx::utid, ColIdx::thread_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 105);
ASSERT_EQ(it.track_id(), t5_id_);
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.utid(), th1_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadEventViewTest, FilterEventUseThreadNoUseTrack) {
auto result = Query({view().ts().ge(103), view().ts().le(105)}, {},
{ColIdx::ts, ColIdx::thread_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 103);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 105);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadEventViewTest, FilterTrackUseEventNoUseThread) {
auto result = Query({view().track_id().eq(t4_id_.value)}, {},
{ColIdx::ts, ColIdx::track_id});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_FALSE(++it);
}
TEST_F(ThreadEventViewTest, FilterEventAndTrack) {
auto result = Query({view().ts().ge(103), view().track_name().eq("t5")}, {},
{ColIdx::ts, ColIdx::track_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 103);
ASSERT_EQ(it.track_name(), Intern("t5"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 105);
ASSERT_EQ(it.track_name(), Intern("t5"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadEventViewTest, FilterEventAndThread) {
auto result = Query({view().ts().ge(103), view().thread_name().eq("th1")}, {},
{ColIdx::ts, ColIdx::thread_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_FALSE(++it);
}
#define PERFETTO_TP_THREAD_SLICE_VIEW_DEF(NAME, FROM, JOIN, COL, _) \
NAME(ViewThreadSliceView, "thread_slice_view") \
COL(id, slice, id) \
COL(ts, slice, ts) \
COL(name, slice, name) \
COL(track_id, slice, track_id) \
COL(track_name, track, name) \
COL(utid, thread, id) \
COL(thread_name, thread, name) \
FROM(ViewSliceTable, slice) \
JOIN(ViewThreadTrackTable, track, id, slice, track_id, View::kNoFlag) \
JOIN(ViewThreadTable, thread, id, track, utid, View::kIdAlwaysPresent)
PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_SLICE_VIEW_DEF);
PERFETTO_TP_DEFINE_VIEW(ViewThreadSliceView);
class ThreadSliceViewTest : public AbstractViewTest<ViewThreadSliceView> {
protected:
ThreadSliceViewTest() {
th1_id_ = thread_.Insert({Intern("th1"), 1}).id;
th2_id_ = thread_.Insert({Intern("th2"), 2}).id;
t1_id_ = track_.Insert({/* name */ Intern("t1")}).id;
t2_id_ = track_.Insert({/* name */ Intern("t2")}).id;
t3_id_ = thread_track_.Insert({/* name */ Intern("t3"), th2_id_}).id;
t4_id_ = thread_track_.Insert({/* name */ Intern("t4"), th1_id_}).id;
t5_id_ = thread_track_.Insert({/* name */ Intern("t5"), th2_id_}).id;
t6_id_ = track_.Insert({/* name */ Intern("t6")}).id;
event_.Insert({/* ts */ 100, t1_id_});
event_.Insert({/* ts */ 101, t2_id_});
slice_table_.Insert({/* ts */ 102, t3_id_, Intern("ts102")});
slice_table_.Insert({/* ts */ 103, t5_id_, Intern("ts103")});
slice_table_.Insert({/* ts */ 104, t4_id_, Intern("ts104")});
event_.Insert({/* ts */ 105, t5_id_});
slice_table_.Insert({/* ts */ 106, t1_id_, Intern("ts106")});
slice_table_.Insert({/* ts */ 107, t4_id_, Intern("ts107")});
}
ViewThreadSliceView& view() override { return slice_view_; }
ViewThreadTable::Id th1_id_;
ViewThreadTable::Id th2_id_;
ViewTrackTable::Id t1_id_;
ViewTrackTable::Id t2_id_;
ViewTrackTable::Id t3_id_;
ViewTrackTable::Id t4_id_;
ViewTrackTable::Id t5_id_;
ViewTrackTable::Id t6_id_;
private:
ViewEventTable event_{&pool_};
ViewSliceTable slice_table_{&pool_, &event_};
ViewTrackTable track_{&pool_};
ViewThreadTrackTable thread_track_{&pool_, &track_};
ViewThreadTable thread_{&pool_};
ViewThreadSliceView slice_view_{&slice_table_, &thread_track_, &thread_};
};
TEST_F(ThreadSliceViewTest, Iterate) {
auto result = Query();
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_EQ(it.track_name(), Intern("t3"));
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 103);
ASSERT_EQ(it.track_name(), Intern("t5"));
ASSERT_EQ(it.track_id(), t5_id_);
ASSERT_EQ(it.utid(), th2_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.track_name(), Intern("t4"));
ASSERT_EQ(it.utid(), th1_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.track_name(), Intern("t4"));
ASSERT_EQ(it.utid(), th1_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadSliceViewTest, FilterAll) {
auto result = Query({view().ts().le(106), view().track_id().le(t4_id_.value),
view().thread_name().eq("th2")},
{}, {ColIdx::ts, ColIdx::track_id, ColIdx::thread_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_FALSE(++it);
}
TEST_F(ThreadSliceViewTest, FilterEventAndTrack) {
auto result = Query({view().ts().le(106), view().track_id().le(t4_id_.value)},
{}, {ColIdx::ts, ColIdx::track_id});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_FALSE(++it);
}
TEST_F(ThreadSliceViewTest, Sort) {
auto result =
Query({}, {view().track_id().ascending(), view().ts().descending()},
{ColIdx::track_id, ColIdx::ts});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 103);
ASSERT_EQ(it.track_id(), t5_id_);
ASSERT_FALSE(++it);
}
TEST_F(ThreadSliceViewTest, FilterAndSort) {
auto result = Query({view().track_id().lt(t5_id_.value)},
{view().track_id().ascending(), view().ts().descending()},
{ColIdx::track_id, ColIdx::ts, ColIdx::thread_name});
auto it = result.IterateRows();
ASSERT_TRUE(it);
ASSERT_EQ(it.ts(), 102);
ASSERT_EQ(it.track_id(), t3_id_);
ASSERT_EQ(it.thread_name(), Intern("th2"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 107);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_TRUE(++it);
ASSERT_EQ(it.ts(), 104);
ASSERT_EQ(it.track_id(), t4_id_);
ASSERT_EQ(it.thread_name(), Intern("th1"));
ASSERT_FALSE(++it);
}
} // namespace
} // namespace tables
} // namespace trace_processor
} // namespace perfetto