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/importers/common/thread_state_tracker.h"
18
19 #include <algorithm>
20
21 #include "src/trace_processor/importers/common/args_tracker.h"
22 #include "src/trace_processor/importers/common/cpu_tracker.h"
23 #include "src/trace_processor/importers/common/global_args_tracker.h"
24 #include "src/trace_processor/importers/common/machine_tracker.h"
25 #include "src/trace_processor/importers/common/process_tracker.h"
26 #include "src/trace_processor/types/trace_processor_context.h"
27 #include "test/gtest_and_gmock.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31 namespace {
32
33 constexpr uint32_t CPU_A = 0;
34 constexpr uint32_t CPU_B = 1;
35 constexpr UniqueTid IDLE_THREAD = 0;
36 constexpr UniqueTid THREAD_A = 1;
37 constexpr UniqueTid THREAD_B = 2;
38 constexpr UniqueTid THREAD_C = 3;
39 static constexpr char kRunning[] = "Running";
40 static constexpr char kRunnable[] = "R";
41 static constexpr char kBlockedFunction[] = "blocked1";
42
43 class ThreadStateTrackerUnittest : public testing::Test {
44 public:
ThreadStateTrackerUnittest()45 ThreadStateTrackerUnittest() {
46 context_.storage.reset(new TraceStorage());
47 context_.process_tracker.reset(new ProcessTracker(&context_));
48 context_.global_args_tracker.reset(
49 new GlobalArgsTracker(context_.storage.get()));
50 context_.machine_tracker.reset(new MachineTracker(&context_, 0));
51 context_.cpu_tracker.reset(new CpuTracker(&context_));
52 context_.args_tracker.reset(new ArgsTracker(&context_));
53 tracker_.reset(new ThreadStateTracker(&context_));
54 }
55
StringIdOf(const char * s)56 StringId StringIdOf(const char* s) {
57 return context_.storage->InternString(s);
58 }
59
ThreadStateIterator()60 tables::ThreadStateTable::ConstIterator ThreadStateIterator() {
61 return context_.storage->thread_state_table().FilterToIterator({});
62 }
63
VerifyThreadState(const tables::ThreadStateTable::ConstIterator & it,int64_t from,std::optional<int64_t> to,UniqueTid utid,const char * state,std::optional<bool> io_wait=std::nullopt,std::optional<StringId> blocked_function=std::nullopt,std::optional<UniqueTid> waker_utid=std::nullopt,std::optional<int64_t> cpu=std::nullopt)64 void VerifyThreadState(
65 const tables::ThreadStateTable::ConstIterator& it,
66 int64_t from,
67 std::optional<int64_t> to,
68 UniqueTid utid,
69 const char* state,
70 std::optional<bool> io_wait = std::nullopt,
71 std::optional<StringId> blocked_function = std::nullopt,
72 std::optional<UniqueTid> waker_utid = std::nullopt,
73 std::optional<int64_t> cpu = std::nullopt) {
74 ASSERT_EQ(it.ts(), from);
75 ASSERT_EQ(it.dur(), to ? *to - from : -1);
76 ASSERT_EQ(it.utid(), utid);
77 if (state == kRunning) {
78 if (cpu.has_value()) {
79 ASSERT_EQ(it.ucpu().value().value, cpu);
80 } else {
81 ASSERT_EQ(it.ucpu().value().value, CPU_A);
82 }
83 } else {
84 ASSERT_EQ(it.ucpu(), std::nullopt);
85 }
86 ASSERT_STREQ(context_.storage->GetString(it.state()).c_str(), state);
87 ASSERT_EQ(it.io_wait(), io_wait);
88 ASSERT_EQ(it.blocked_function(), blocked_function);
89 ASSERT_EQ(it.waker_utid(), waker_utid);
90 }
91
92 protected:
93 std::unique_ptr<ThreadStateTracker> tracker_;
94 TraceProcessorContext context_;
95 StringId running_string_id_;
96 StringId runnable_string_id_;
97 StringId sched_blocked_reason_id_;
98 };
99
TEST_F(ThreadStateTrackerUnittest,BasicPushSchedSwitchEvent)100 TEST_F(ThreadStateTrackerUnittest, BasicPushSchedSwitchEvent) {
101 tracker_->PushSchedSwitchEvent(10, CPU_A, THREAD_A, StringIdOf("S"),
102 THREAD_B);
103
104 ASSERT_EQ(context_.storage->thread_state_table().row_count(), 2ul);
105 auto rows_it = ThreadStateIterator();
106 VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, "S");
107 VerifyThreadState(++rows_it, 10, std::nullopt, THREAD_B, kRunning);
108 }
109
TEST_F(ThreadStateTrackerUnittest,StartWithWakingEvent)110 TEST_F(ThreadStateTrackerUnittest, StartWithWakingEvent) {
111 tracker_->PushWakingEvent(10, THREAD_A, THREAD_C);
112 ASSERT_EQ(context_.storage->thread_state_table().row_count(), 1ul);
113 }
114
TEST_F(ThreadStateTrackerUnittest,BasicWakingEvent)115 TEST_F(ThreadStateTrackerUnittest, BasicWakingEvent) {
116 tracker_->PushSchedSwitchEvent(10, CPU_A, THREAD_A, StringIdOf("S"),
117 THREAD_B);
118 tracker_->PushWakingEvent(20, THREAD_A, THREAD_C);
119
120 ASSERT_EQ(context_.storage->thread_state_table().row_count(), 3ul);
121 auto row_it = ThreadStateIterator();
122 VerifyThreadState(row_it, 10, 20, THREAD_A, "S");
123 VerifyThreadState(++row_it, 10, std::nullopt, THREAD_B, kRunning);
124 VerifyThreadState(++row_it, 20, std::nullopt, THREAD_A, kRunnable,
125 std::nullopt, std::nullopt, THREAD_C);
126 }
127
TEST_F(ThreadStateTrackerUnittest,BasicPushBlockedReason)128 TEST_F(ThreadStateTrackerUnittest, BasicPushBlockedReason) {
129 tracker_->PushSchedSwitchEvent(10, CPU_A, THREAD_A, StringIdOf("S"),
130 THREAD_B);
131 tracker_->PushBlockedReason(THREAD_A, true, StringIdOf(kBlockedFunction));
132
133 auto rows_it = ThreadStateIterator();
134 VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, "S", true,
135 StringIdOf(kBlockedFunction));
136 }
137
TEST_F(ThreadStateTrackerUnittest,CloseState)138 TEST_F(ThreadStateTrackerUnittest, CloseState) {
139 // Add a new runnable state of THREAD_A at ts=10.
140 tracker_->PushSchedSwitchEvent(10, CPU_A, THREAD_A, StringIdOf(kRunnable),
141 THREAD_B);
142
143 // Close the runnable state of THREAD_A at ts=20 and make it run on the CPU.
144 tracker_->PushSchedSwitchEvent(20, CPU_A, THREAD_B, StringIdOf("S"),
145 THREAD_A);
146
147 auto rows_it = ThreadStateIterator();
148 VerifyThreadState(rows_it, 10, 20, THREAD_A, kRunnable);
149 VerifyThreadState(++rows_it, 10, 20, THREAD_B, kRunning);
150 }
151
TEST_F(ThreadStateTrackerUnittest,PushIdleThread)152 TEST_F(ThreadStateTrackerUnittest, PushIdleThread) {
153 tracker_->PushSchedSwitchEvent(10, CPU_A, IDLE_THREAD, StringIdOf(kRunnable),
154 THREAD_A);
155 auto rows_it = ThreadStateIterator();
156
157 // The opening of idle_thred should be discarded so the first row will be
158 // for the THREAD_A.
159 VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, kRunning);
160 }
161
TEST_F(ThreadStateTrackerUnittest,SchedBlockedReasonWithIdleThread)162 TEST_F(ThreadStateTrackerUnittest, SchedBlockedReasonWithIdleThread) {
163 tracker_->PushSchedSwitchEvent(1, CPU_A, IDLE_THREAD, StringIdOf("D"),
164 THREAD_A);
165 tracker_->PushSchedSwitchEvent(2, CPU_A, THREAD_A, StringIdOf("D"),
166 IDLE_THREAD);
167 tracker_->PushBlockedReason(THREAD_A, IDLE_THREAD, std::nullopt);
168 tracker_->PushSchedSwitchEvent(3, CPU_A, IDLE_THREAD, StringIdOf("D"),
169 THREAD_B);
170 tracker_->PushSchedSwitchEvent(4, CPU_A, THREAD_B, StringIdOf("D"),
171 IDLE_THREAD);
172 tracker_->PushBlockedReason(THREAD_B, 1, std::nullopt);
173
174 auto rows_it = ThreadStateIterator();
175
176 VerifyThreadState(rows_it, 1, 2, THREAD_A, kRunning);
177 VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_A, "D", 0);
178 VerifyThreadState(++rows_it, 3, 4, THREAD_B, kRunning);
179 VerifyThreadState(++rows_it, 4, std::nullopt, THREAD_B, "D", 1);
180 }
181
TEST_F(ThreadStateTrackerUnittest,SchedSwitchForcedMigration)182 TEST_F(ThreadStateTrackerUnittest, SchedSwitchForcedMigration) {
183 tracker_->PushSchedSwitchEvent(1, CPU_A, THREAD_A, StringIdOf("S"), THREAD_B);
184 tracker_->PushSchedSwitchEvent(2, CPU_A, THREAD_A, StringIdOf("S"), THREAD_B);
185
186 auto rows_it = ThreadStateIterator();
187 VerifyThreadState(rows_it, 1, std::nullopt, THREAD_A, "S");
188 VerifyThreadState(++rows_it, 1, 2, THREAD_B, kRunning);
189 }
190
TEST_F(ThreadStateTrackerUnittest,SchedWakingBigTest)191 TEST_F(ThreadStateTrackerUnittest, SchedWakingBigTest) {
192 tracker_->PushWakingEvent(1, 8, 11);
193 tracker_->PushSchedSwitchEvent(2, CPU_A, 0, StringIdOf(kRunnable), 8);
194 tracker_->PushSchedSwitchEvent(2, CPU_A, 11, StringIdOf("S"), 0);
195 tracker_->PushSchedSwitchEvent(3, CPU_A, 8, StringIdOf("S"), 0);
196 tracker_->PushSchedSwitchEvent(4, CPU_A, 17771, StringIdOf("S"), 17772);
197 tracker_->PushSchedSwitchEvent(5, CPU_A, 17772, StringIdOf("S"), 0);
198 tracker_->PushWakingEvent(6, 18, 0);
199 tracker_->PushSchedSwitchEvent(7, CPU_A, 0, StringIdOf(kRunnable), 18);
200
201 auto rows_it = ThreadStateIterator();
202 VerifyThreadState(rows_it, 1, 2, 8, kRunnable, std::nullopt, std::nullopt,
203 11);
204 VerifyThreadState(++rows_it, 2, 3, 8, kRunning);
205 VerifyThreadState(++rows_it, 2, std::nullopt, 11, "S");
206 VerifyThreadState(++rows_it, 3, std::nullopt, 8, "S");
207 VerifyThreadState(++rows_it, 4, std::nullopt, 17771, "S");
208 VerifyThreadState(++rows_it, 4, 5, 17772, kRunning);
209 VerifyThreadState(++rows_it, 5, std::nullopt, 17772, "S");
210 VerifyThreadState(++rows_it, 6, 7, 18, kRunnable, std::nullopt, std::nullopt,
211 0);
212 VerifyThreadState(++rows_it, 7, std::nullopt, 18, kRunning);
213 }
214
TEST_F(ThreadStateTrackerUnittest,RunningOnMultipleCPUsForcedMigration)215 TEST_F(ThreadStateTrackerUnittest, RunningOnMultipleCPUsForcedMigration) {
216 // Thread A was running on multiple CPUs
217 tracker_->PushSchedSwitchEvent(1, CPU_A, THREAD_C, StringIdOf("S"), THREAD_A);
218 tracker_->PushSchedSwitchEvent(2, CPU_B, THREAD_B, StringIdOf("S"), THREAD_A);
219
220 auto rows_it = ThreadStateIterator();
221 VerifyThreadState(rows_it, 1, std::nullopt, THREAD_C, "S");
222 VerifyThreadState(++rows_it, 1, 2, THREAD_A, kRunning);
223 VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_B, "S");
224 VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_A, kRunning,
225 std::nullopt, std::nullopt, std::nullopt, CPU_B);
226 }
227
228 } // namespace
229 } // namespace trace_processor
230 } // namespace perfetto
231