| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/trace_processor/ftrace_utils.h" |
| |
| #include <stdint.h> |
| #include <algorithm> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_writer.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| namespace ftrace_utils { |
| |
| namespace { |
| struct FtraceTime { |
| FtraceTime(int64_t ns) |
| : secs(ns / 1000000000LL), micros((ns - secs * 1000000000LL) / 1000) {} |
| |
| const int64_t secs; |
| const int64_t micros; |
| }; |
| } // namespace |
| |
| TaskState::TaskState(uint16_t raw_state) { |
| if (raw_state > kMaxState) { |
| state_ = 0; |
| } else { |
| state_ = raw_state | kValid; |
| } |
| } |
| |
| TaskState::TaskState(const char* state_str) { |
| bool invalid_char = false; |
| bool is_runnable = false; |
| for (size_t i = 0; state_str[i] != '\0'; i++) { |
| char c = state_str[i]; |
| if (is_kernel_preempt()) { |
| // No other character should be encountered after '+'. |
| invalid_char = true; |
| break; |
| } else if (c == '+') { |
| state_ |= kMaxState; |
| continue; |
| } |
| |
| if (is_runnable) { |
| // We should not encounter any character apart from '+' if runnable. |
| invalid_char = true; |
| break; |
| } |
| |
| if (c == 'R') { |
| if (state_ != 0) { |
| // We should not encounter R if we already have set other atoms. |
| invalid_char = true; |
| break; |
| } else { |
| is_runnable = true; |
| continue; |
| } |
| } |
| |
| if (c == 'S') |
| state_ |= Atom::kInterruptibleSleep; |
| else if (c == 'D') |
| state_ |= Atom::kUninterruptibleSleep; |
| else if (c == 'T') |
| state_ |= Atom::kStopped; |
| else if (c == 't') |
| state_ |= Atom::kTraced; |
| else if (c == 'X') |
| state_ |= Atom::kExitDead; |
| else if (c == 'Z') |
| state_ |= Atom::kExitZombie; |
| else if (c == 'x') |
| state_ |= Atom::kTaskDead; |
| else if (c == 'K') |
| state_ |= Atom::kWakeKill; |
| else if (c == 'W') |
| state_ |= Atom::kWaking; |
| else if (c == 'P') |
| state_ |= Atom::kParked; |
| else if (c == 'N') |
| state_ |= Atom::kNoLoad; |
| else if (c == '|') |
| continue; |
| else { |
| invalid_char = true; |
| break; |
| } |
| } |
| |
| bool no_state = !is_runnable && state_ == 0; |
| if (invalid_char || no_state || state_ > kMaxState) { |
| state_ = 0; |
| } else { |
| state_ |= kValid; |
| } |
| } |
| |
| TaskState::TaskStateStr TaskState::ToString(char separator) const { |
| PERFETTO_CHECK(is_valid()); |
| |
| char buffer[32]; |
| size_t pos = 0; |
| |
| // This mapping is given by the file |
| // https://android.googlesource.com/kernel/msm.git/+/android-msm-wahoo-4.4-pie-qpr1/include/trace/events/sched.h#155 |
| if (is_runnable()) { |
| buffer[pos++] = 'R'; |
| } else { |
| if (state_ & Atom::kInterruptibleSleep) |
| buffer[pos++] = 'S'; |
| if (state_ & Atom::kUninterruptibleSleep) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'D'; // D for (D)isk sleep |
| } |
| if (state_ & Atom::kStopped) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'T'; |
| } |
| if (state_ & Atom::kTraced) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 't'; |
| } |
| if (state_ & Atom::kExitDead) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'X'; |
| } |
| if (state_ & Atom::kExitZombie) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'Z'; |
| } |
| if (state_ & Atom::kTaskDead) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'x'; |
| } |
| if (state_ & Atom::kWakeKill) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'K'; |
| } |
| if (state_ & Atom::kWaking) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'W'; |
| } |
| if (state_ & Atom::kParked) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'P'; |
| } |
| if (state_ & Atom::kNoLoad) { |
| if (separator && pos != 0) |
| buffer[pos++] = separator; |
| buffer[pos++] = 'N'; |
| } |
| } |
| |
| if (is_kernel_preempt()) |
| buffer[pos++] = '+'; |
| |
| TaskStateStr output{}; |
| memcpy(output.data(), buffer, std::min(pos, output.size() - 1)); |
| return output; |
| } |
| |
| void FormatSystracePrefix(int64_t timestamp, |
| uint32_t cpu, |
| uint32_t pid, |
| uint32_t tgid, |
| base::StringView name, |
| base::StringWriter* writer) { |
| FtraceTime ftrace_time(timestamp); |
| if (pid == 0) { |
| name = "<idle>"; |
| } else if (name == "") { |
| name = "<unknown>"; |
| } else if (name == "CrRendererMain") { |
| // TODO(taylori): Remove this when crbug.com/978093 is fixed or |
| // when a better solution is found. |
| name = "CrRendererMainThread"; |
| } |
| |
| int64_t padding = 16 - static_cast<int64_t>(name.size()); |
| if (padding > 0) { |
| writer->AppendChar(' ', static_cast<size_t>(padding)); |
| } |
| for (size_t i = 0; i < name.size(); ++i) { |
| char c = name.data()[i]; |
| writer->AppendChar(c == '-' ? '_' : c); |
| } |
| writer->AppendChar('-'); |
| |
| size_t pre_pid_pos = writer->pos(); |
| writer->AppendInt(pid); |
| size_t pid_chars = writer->pos() - pre_pid_pos; |
| if (PERFETTO_LIKELY(pid_chars < 5)) { |
| writer->AppendChar(' ', 5 - pid_chars); |
| } |
| |
| writer->AppendLiteral(" ("); |
| if (tgid == 0) { |
| writer->AppendLiteral("-----"); |
| } else { |
| writer->AppendPaddedInt<' ', 5>(tgid); |
| } |
| writer->AppendLiteral(") ["); |
| writer->AppendPaddedInt<'0', 3>(cpu); |
| writer->AppendLiteral("] .... "); |
| |
| writer->AppendInt(ftrace_time.secs); |
| writer->AppendChar('.'); |
| writer->AppendPaddedInt<'0', 6>(ftrace_time.micros); |
| writer->AppendChar(':'); |
| } |
| |
| } // namespace ftrace_utils |
| } // namespace trace_processor |
| } // namespace perfetto |