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