• 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 #include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
17 
18 namespace perfetto {
19 namespace trace_processor {
ThreadStateTracker(TraceStorage * storage)20 ThreadStateTracker::ThreadStateTracker(TraceStorage* storage)
21     : storage_(storage),
22       running_string_id_(storage->InternString("Running")),
23       runnable_string_id_(storage->InternString("R")) {}
24 ThreadStateTracker::~ThreadStateTracker() = default;
25 
PushSchedSwitchEvent(int64_t event_ts,uint32_t cpu,UniqueTid prev_utid,StringId prev_state,UniqueTid next_utid)26 void ThreadStateTracker::PushSchedSwitchEvent(int64_t event_ts,
27                                               uint32_t cpu,
28                                               UniqueTid prev_utid,
29                                               StringId prev_state,
30                                               UniqueTid next_utid) {
31   // Code related to previous utid. If the thread wasn't running before we know
32   // we lost data and should close the slice accordingly.
33   bool data_loss_cond =
34       HasPreviousRowNumbersForUtid(prev_utid) &&
35       !IsRunning(RowNumToRef(prev_row_numbers_for_thread_[prev_utid]->last_row)
36                      .state());
37   ClosePendingState(event_ts, prev_utid, data_loss_cond);
38   AddOpenState(event_ts, prev_utid, prev_state);
39 
40   // Code related to next utid.
41   // Due to forced migration, it is possible for the same thread to be
42   // scheduled on different CPUs at the same time.
43   // We work around this problem by truncating the previous state to the start
44   // of this state and starting the next state normally. This is why we don't
45   // check whether previous state is running/runnable. See b/186509316 for
46   // details and an example on when this happens.
47   ClosePendingState(event_ts, next_utid, false);
48   AddOpenState(event_ts, next_utid, running_string_id_, cpu);
49 }
50 
PushWakingEvent(int64_t event_ts,UniqueTid utid,UniqueTid waker_utid)51 void ThreadStateTracker::PushWakingEvent(int64_t event_ts,
52                                          UniqueTid utid,
53                                          UniqueTid waker_utid) {
54   // Only open new runnable state if thread already had a sched switch event.
55   if (!HasPreviousRowNumbersForUtid(utid)) {
56     return;
57   }
58   auto last_row_ref = RowNumToRef(prev_row_numbers_for_thread_[utid]->last_row);
59 
60   // Occasionally, it is possible to get a waking event for a thread
61   // which is already in a runnable state. When this happens (or if the thread
62   // is running), we just ignore the waking event. See b/186509316 for details
63   // and an example on when this happens. Only blocked events can be waken up.
64   if (!IsBlocked(last_row_ref.state())) {
65     return;
66   }
67 
68   // Close the sleeping state and open runnable state.
69   ClosePendingState(event_ts, utid, false);
70   AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
71 }
72 
PushNewTaskEvent(int64_t event_ts,UniqueTid utid,UniqueTid waker_utid)73 void ThreadStateTracker::PushNewTaskEvent(int64_t event_ts,
74                                          UniqueTid utid,
75                                          UniqueTid waker_utid) {
76   AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
77 }
78 
PushBlockedReason(UniqueTid utid,std::optional<bool> io_wait,std::optional<StringId> blocked_function)79 void ThreadStateTracker::PushBlockedReason(
80     UniqueTid utid,
81     std::optional<bool> io_wait,
82     std::optional<StringId> blocked_function) {
83   // Return if there is no state, as there is are no previous rows available.
84   if (!HasPreviousRowNumbersForUtid(utid))
85     return;
86 
87   // Return if no previous bocked row exists.
88   auto blocked_row_number =
89       prev_row_numbers_for_thread_[utid]->last_blocked_row;
90   if (!blocked_row_number.has_value())
91     return;
92 
93   auto row_reference = RowNumToRef(blocked_row_number.value());
94   if (io_wait.has_value()) {
95     row_reference.set_io_wait(*io_wait);
96   }
97   if (blocked_function.has_value()) {
98     row_reference.set_blocked_function(*blocked_function);
99   }
100 }
101 
AddOpenState(int64_t ts,UniqueTid utid,StringId state,std::optional<uint32_t> cpu,std::optional<UniqueTid> waker_utid)102 void ThreadStateTracker::AddOpenState(int64_t ts,
103                                       UniqueTid utid,
104                                       StringId state,
105                                       std::optional<uint32_t> cpu,
106                                       std::optional<UniqueTid> waker_utid) {
107   // Ignore utid 0 because it corresponds to the swapper thread which doesn't
108   // make sense to insert.
109   if (utid == 0)
110     return;
111 
112   // Insert row with unfinished state
113   tables::ThreadStateTable::Row row;
114   row.ts = ts;
115   row.cpu = cpu;
116   row.waker_utid = waker_utid;
117   row.dur = -1;
118   row.utid = utid;
119   row.state = state;
120   auto row_num = storage_->mutable_thread_state_table()->Insert(row).row_number;
121 
122   if (utid >= prev_row_numbers_for_thread_.size()) {
123     prev_row_numbers_for_thread_.resize(utid + 1);
124   }
125 
126   if (!prev_row_numbers_for_thread_[utid].has_value()) {
127     prev_row_numbers_for_thread_[utid] = RelatedRows{std::nullopt, row_num};
128   }
129 
130   if (IsRunning(state)) {
131     prev_row_numbers_for_thread_[utid] = RelatedRows{std::nullopt, row_num};
132   } else if (IsBlocked(state)) {
133     prev_row_numbers_for_thread_[utid] = RelatedRows{row_num, row_num};
134   } else /* if (IsRunnable(state)) */ {
135     prev_row_numbers_for_thread_[utid]->last_row = row_num;
136   }
137 }
138 
ClosePendingState(int64_t end_ts,UniqueTid utid,bool data_loss)139 void ThreadStateTracker::ClosePendingState(int64_t end_ts,
140                                            UniqueTid utid,
141                                            bool data_loss) {
142   // Discard close if there is no open state to close.
143   if (!HasPreviousRowNumbersForUtid(utid))
144     return;
145 
146   auto row_ref = RowNumToRef(prev_row_numbers_for_thread_[utid]->last_row);
147 
148   // Update the duration only for states without data loss.
149   if (!data_loss) {
150     row_ref.set_dur(end_ts - row_ref.ts());
151   }
152 }
153 
IsRunning(StringId state)154 bool ThreadStateTracker::IsRunning(StringId state) {
155   return state == running_string_id_;
156 }
157 
IsRunnable(StringId state)158 bool ThreadStateTracker::IsRunnable(StringId state) {
159   return state == runnable_string_id_;
160 }
161 
IsBlocked(StringId state)162 bool ThreadStateTracker::IsBlocked(StringId state) {
163   return !(IsRunnable(state) || IsRunning(state));
164 }
165 
166 }  // namespace trace_processor
167 }  // namespace perfetto
168