1 /*
2 * Copyright (C) 2019 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/types/task_state.h"
18
19 #include <string.h>
20
21 #include "perfetto/base/logging.h"
22
23 namespace perfetto {
24 namespace trace_processor {
25 namespace ftrace_utils {
26
27 // static
FromRawPrevState(uint16_t raw_state,std::optional<VersionNumber> kernel_version)28 TaskState TaskState::FromRawPrevState(
29 uint16_t raw_state,
30 std::optional<VersionNumber> kernel_version) {
31 return TaskState(raw_state, kernel_version);
32 }
33
34 // static
FromSystrace(const char * state_str)35 TaskState TaskState::FromSystrace(const char* state_str) {
36 return TaskState(state_str);
37 }
38
39 // static
FromParsedFlags(uint16_t parsed_state)40 TaskState TaskState::FromParsedFlags(uint16_t parsed_state) {
41 TaskState ret;
42 ret.parsed_ = parsed_state;
43 return ret;
44 }
45
46 // See header for extra details.
47 //
48 // Note to maintainers: going forward, the most likely "breaking" changes are:
49 // * a new flag is added to TASK_REPORT (see include/linux/sched.h kernel src)
50 // * a new report-specific flag is added above TASK_REPORT
51 // In both cases, this will change the value of TASK_REPORT_MAX that is used to
52 // report preemption in sched_switch. We'll need to modify this class to keep
53 // up, or make traced_probes record the sched_switch format string in traces.
54 //
55 // Note to maintainers: if changing the default kernel assumption or the 4.4
56 // codepath, you'll need to update ToRawStateOnlyForSystraceConversions().
TaskState(uint16_t raw_state,std::optional<VersionNumber> opt_version)57 TaskState::TaskState(uint16_t raw_state,
58 std::optional<VersionNumber> opt_version) {
59 // Values up to and including 0x20 (EXIT_ZOMBIE) never changed, so map them
60 // directly onto ParsedFlag (we use the same flag bits for convenience).
61 parsed_ = raw_state & (0x40 - 1);
62
63 // Parsing upper bits depends on kernel version. Default to 4.4 because old
64 // perfetto traces don't record kernel version.
65 auto version = VersionNumber{4, 4};
66 if (opt_version) {
67 version = opt_version.value();
68 }
69
70 // Kernels 4.14+: flags up to and including 0x40 (TASK_PARKED) are reported
71 // with their scheduler values. Whereas flags 0x80 (normally TASK_DEAD) and
72 // above are masked off and repurposed for reporting-specific values.
73 if (version >= VersionNumber{4, 14}) {
74 if (raw_state & 0x40) // TASK_PARKED
75 parsed_ |= kParked;
76
77 // REPORT_TASK_IDLE (0x80), which reports the TASK_IDLE composite state
78 // (TASK_UNINTERRUPTIBLE | TASK_NOLOAD):
79 if (raw_state & 0x80) {
80 parsed_ |= kIdle;
81 }
82
83 // REPORT_TASK_MAX that sched_switch uses to report preemption. At the time
84 // of writing 0x100 because REPORT_TASK_IDLE is the only report-specific
85 // flag:
86 if (raw_state & 0x100)
87 parsed_ |= kPreempted;
88
89 // Attempt to notice REPORT_TASK_MAX changing. If this dcheck fires, please
90 // file a bug report against perfetto. Exactly 4.14 kernels are excluded
91 // from the dcheck since there are known instances of such kernels that
92 // still use the old flag mask in practice. So we'll still mark the states
93 // as invalid but not crash debug builds.
94 if (raw_state & 0xfe00) {
95 parsed_ = kInvalid;
96 PERFETTO_DCHECK((version == VersionNumber{4, 14}));
97 }
98 return;
99 }
100
101 // Before 4.14, sched_switch reported the full set of scheduler flags
102 // (without masking down to TASK_REPORT). Note: several flags starting at
103 // 0x40 have a different value to the above because 4.14 reordered them.
104 // See https://github.com/torvalds/linux/commit/8ef9925b02.
105 if (raw_state & 0x40) // TASK_DEAD
106 parsed_ |= kTaskDead;
107 if (raw_state & 0x80) // TASK_WAKEKILL
108 parsed_ |= kWakeKill;
109 if (raw_state & 0x100) // TASK_WAKING
110 parsed_ |= kWaking;
111 if (raw_state & 0x200) // TASK_PARKED
112 parsed_ |= kParked;
113 if (raw_state & 0x400) // TASK_NOLOAD
114 parsed_ |= kNoLoad;
115
116 // Convert kUninterruptibleSleep+kNoLoad into kIdle since that's what it
117 // means, and the UI can present the latter better.
118 // See https://github.com/torvalds/linux/commit/80ed87c8a9ca.
119 if (parsed_ == (kUninterruptibleSleep | kNoLoad)) {
120 parsed_ = kIdle;
121 }
122
123 // Kernel version range [4.8, 4.14) has TASK_NEW, hence preemption
124 // (TASK_STATE_MAX) is 0x1000. We don't decode TASK_NEW itself since it will
125 // never show up in sched_switch.
126 if (version >= VersionNumber{4, 8}) {
127 if (raw_state & 0x1000)
128 parsed_ |= kPreempted;
129 } else {
130 // Kernel (..., 4.8), preemption (TASK_STATE_MAX) is 0x800. Assume all
131 // kernels in this range have the 4.4 state of the bitmask. This is most
132 // likely incorrect on <4.2 as that's when TASK_NOLOAD was introduced
133 // (which means preemption is reported at a different bit).
134 if (raw_state & 0x800)
135 parsed_ |= kPreempted;
136 }
137 }
138
ToString(char separator) const139 TaskState::TaskStateStr TaskState::ToString(char separator) const {
140 if (!is_valid()) {
141 return TaskStateStr{"?"};
142 }
143
144 // Character aliases follow sched_switch's format string.
145 char buffer[32];
146 size_t pos = 0;
147 if (is_runnable()) {
148 buffer[pos++] = 'R';
149 if (parsed_ & kPreempted) {
150 buffer[pos++] = '+';
151 PERFETTO_DCHECK(parsed_ == kPreempted);
152 }
153 } else {
154 auto append = [&](ParsedFlag flag, char c) {
155 if (!(parsed_ & flag))
156 return;
157 if (separator && pos != 0)
158 buffer[pos++] = separator;
159 buffer[pos++] = c;
160 };
161 append(kInterruptibleSleep, 'S');
162 append(kUninterruptibleSleep, 'D'); // (D)isk sleep
163 append(kStopped, 'T');
164 append(kTraced, 't');
165 append(kExitDead, 'X');
166 append(kExitZombie, 'Z');
167 append(kParked, 'P');
168 append(kTaskDead, 'x');
169 append(kWakeKill, 'K');
170 append(kWaking, 'W');
171 append(kNoLoad, 'N');
172 append(kIdle, 'I');
173 }
174
175 TaskStateStr output{};
176 size_t sz = (pos < output.size() - 1) ? pos : output.size() - 1;
177 memcpy(output.data(), buffer, sz);
178 return output;
179 }
180
181 // Used when parsing systrace, i.e. textual ftrace output.
TaskState(const char * state_str)182 TaskState::TaskState(const char* state_str) {
183 parsed_ = 0;
184 if (!state_str || state_str[0] == '\0') {
185 parsed_ = kInvalid;
186 return;
187 }
188
189 // R or R+, otherwise invalid
190 if (state_str[0] == 'R') {
191 parsed_ = kRunnable;
192 if (!strncmp(state_str, "R+", 3))
193 parsed_ |= kPreempted;
194 return;
195 }
196
197 for (size_t i = 0; state_str[i] != '\0'; i++) {
198 char c = state_str[i];
199 if (c == 'R' || c == '+') {
200 parsed_ = kInvalid;
201 return;
202 }
203 if (c == '|')
204 continue;
205
206 auto parse = [&](ParsedFlag flag, char symbol) {
207 if (c == symbol)
208 parsed_ |= flag;
209 return c == symbol;
210 };
211 bool recognized = false;
212 recognized |= parse(kInterruptibleSleep, 'S');
213 recognized |= parse(kUninterruptibleSleep, 'D'); // (D)isk sleep
214 recognized |= parse(kStopped, 'T');
215 recognized |= parse(kTraced, 't');
216 recognized |= parse(kExitDead, 'X');
217 recognized |= parse(kExitZombie, 'Z');
218 recognized |= parse(kParked, 'P');
219 recognized |= parse(kTaskDead, 'x');
220 recognized |= parse(kWakeKill, 'K');
221 recognized |= parse(kWaking, 'W');
222 recognized |= parse(kNoLoad, 'N');
223 recognized |= parse(kIdle, 'I');
224 if (!recognized) {
225 parsed_ = kInvalid;
226 return;
227 }
228 }
229 }
230
231 // Hard-assume 4.4 flag layout per the header rationale.
ToRawStateOnlyForSystraceConversions() const232 uint16_t TaskState::ToRawStateOnlyForSystraceConversions() const {
233 if (parsed_ == kInvalid)
234 return 0xffff;
235
236 if (parsed_ == kPreempted)
237 return 0x0800;
238
239 uint16_t ret = parsed_ & (0x40 - 1);
240 if (parsed_ & kTaskDead)
241 ret |= 0x40;
242 if (parsed_ & kWakeKill)
243 ret |= 0x80;
244 if (parsed_ & kWaking)
245 ret |= 0x100;
246 if (parsed_ & kParked)
247 ret |= 0x200;
248 if (parsed_ & kNoLoad)
249 ret |= 0x400;
250
251 // Expand kIdle into the underlying kUninterruptibleSleep + kNoLoad.
252 if (parsed_ & kIdle)
253 ret |= (0x2 | 0x400);
254
255 return ret;
256 }
257
258 } // namespace ftrace_utils
259 } // namespace trace_processor
260 } // namespace perfetto
261