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 <stdint.h>
20 #include <algorithm>
21
22 #include "perfetto/base/logging.h"
23 #include "perfetto/ext/base/string_writer.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27 namespace ftrace_utils {
28
TaskState(uint16_t raw_state,base::Optional<VersionNumber> opt_version)29 TaskState::TaskState(uint16_t raw_state,
30 base::Optional<VersionNumber> opt_version) {
31 auto version = VersionNumber{4, 4};
32 if (opt_version) {
33 version = opt_version.value();
34 }
35 max_state_ = version < VersionNumber{4, 9} ? 2048 : 4096;
36
37 if (raw_state > max_state_) {
38 state_ = 0;
39 } else {
40 state_ |= kValid;
41 }
42
43 if (version < VersionNumber{4, 14}) {
44 state_ |= raw_state;
45 return;
46 }
47 // All values below kTaskDead are consistent between kernels.
48 state_ |= raw_state & (kTaskDead - 1);
49
50 // Only values up to 0x80 (plus max_state) are relevant in kernels >= 4.14.
51 // See
52 // https://android.googlesource.com/kernel/msm.git/+/refs/heads/android-msm-coral-4.14-android10-qpr1/include/trace/events/sched.h#219
53 if (raw_state & 0x40) {
54 state_ |= kParked;
55 }
56 if (raw_state & 0x80) {
57 state_ |= kTaskDead;
58 }
59 if (raw_state & max_state_) {
60 state_ |= max_state_;
61 }
62 }
63
TaskState(const char * state_str)64 TaskState::TaskState(const char* state_str) {
65 bool invalid_char = false;
66 bool is_runnable = false;
67 for (size_t i = 0; state_str[i] != '\0'; i++) {
68 char c = state_str[i];
69 if (is_kernel_preempt()) {
70 // No other character should be encountered after '+'.
71 invalid_char = true;
72 break;
73 } else if (c == '+') {
74 state_ |= max_state_;
75 continue;
76 }
77
78 if (is_runnable) {
79 // We should not encounter any character apart from '+' if runnable.
80 invalid_char = true;
81 break;
82 }
83
84 if (c == 'R') {
85 if (state_ != 0) {
86 // We should not encounter R if we already have set other atoms.
87 invalid_char = true;
88 break;
89 } else {
90 is_runnable = true;
91 continue;
92 }
93 }
94
95 if (c == 'S')
96 state_ |= Atom::kInterruptibleSleep;
97 else if (c == 'D')
98 state_ |= Atom::kUninterruptibleSleep;
99 else if (c == 'T')
100 state_ |= Atom::kStopped;
101 else if (c == 't')
102 state_ |= Atom::kTraced;
103 else if (c == 'X')
104 state_ |= Atom::kExitDead;
105 else if (c == 'Z')
106 state_ |= Atom::kExitZombie;
107 else if (c == 'x' || c == 'I')
108 // On Linux kernels 4.14+, the character for task dead changed
109 // from 'x' to 'I'.
110 state_ |= Atom::kTaskDead;
111 else if (c == 'K')
112 state_ |= Atom::kWakeKill;
113 else if (c == 'W')
114 state_ |= Atom::kWaking;
115 else if (c == 'P')
116 state_ |= Atom::kParked;
117 else if (c == 'N')
118 state_ |= Atom::kNoLoad;
119 else if (c == '|')
120 continue;
121 else {
122 invalid_char = true;
123 break;
124 }
125 }
126 bool no_state = !is_runnable && state_ == 0;
127 if (invalid_char || no_state || state_ > max_state_) {
128 state_ = 0;
129 } else {
130 state_ |= kValid;
131 }
132 }
133
ToString(char separator) const134 TaskState::TaskStateStr TaskState::ToString(char separator) const {
135 if (!is_valid()) {
136 return TaskStateStr{"?"};
137 }
138
139 char buffer[32];
140 size_t pos = 0;
141
142 // This mapping is given by the file
143 // https://android.googlesource.com/kernel/msm.git/+/android-msm-wahoo-4.4-pie-qpr1/include/trace/events/sched.h#155
144 // Some of these flags are ignored in later kernels but we output them anyway.
145 if (is_runnable()) {
146 buffer[pos++] = 'R';
147 } else {
148 if (state_ & Atom::kInterruptibleSleep)
149 buffer[pos++] = 'S';
150 if (state_ & Atom::kUninterruptibleSleep) {
151 if (separator && pos != 0)
152 buffer[pos++] = separator;
153 buffer[pos++] = 'D'; // D for (D)isk sleep
154 }
155 if (state_ & Atom::kStopped) {
156 if (separator && pos != 0)
157 buffer[pos++] = separator;
158 buffer[pos++] = 'T';
159 }
160 if (state_ & Atom::kTraced) {
161 if (separator && pos != 0)
162 buffer[pos++] = separator;
163 buffer[pos++] = 't';
164 }
165 if (state_ & Atom::kExitDead) {
166 if (separator && pos != 0)
167 buffer[pos++] = separator;
168 buffer[pos++] = 'X';
169 }
170 if (state_ & Atom::kExitZombie) {
171 if (separator && pos != 0)
172 buffer[pos++] = separator;
173 buffer[pos++] = 'Z';
174 }
175 if (state_ & Atom::kTaskDead) {
176 if (separator && pos != 0)
177 buffer[pos++] = separator;
178 buffer[pos++] = 'I';
179 }
180 if (state_ & Atom::kWakeKill) {
181 if (separator && pos != 0)
182 buffer[pos++] = separator;
183 buffer[pos++] = 'K';
184 }
185 if (state_ & Atom::kWaking) {
186 if (separator && pos != 0)
187 buffer[pos++] = separator;
188 buffer[pos++] = 'W';
189 }
190 if (state_ & Atom::kParked) {
191 if (separator && pos != 0)
192 buffer[pos++] = separator;
193 buffer[pos++] = 'P';
194 }
195 if (state_ & Atom::kNoLoad) {
196 if (separator && pos != 0)
197 buffer[pos++] = separator;
198 buffer[pos++] = 'N';
199 }
200 }
201
202 if (is_kernel_preempt())
203 buffer[pos++] = '+';
204
205 TaskStateStr output{};
206 memcpy(output.data(), buffer, std::min(pos, output.size() - 1));
207 return output;
208 }
209
210 } // namespace ftrace_utils
211 } // namespace trace_processor
212 } // namespace perfetto
213