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