xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/common/thread_state_tracker_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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