blob: b58fd73c0dded76926ca6e61f78e930d0bbbdee6 [file] [log] [blame]
/*
* 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