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