blob: 029649ed9d6883d625c3a7bfe722d4e8c99efeee [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.
*/
#ifndef SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_
#define SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_
#include <stdint.h>
#include "perfetto/ext/base/string_view.h"
#include "perfetto/protozero/packed_repeated_fields.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
#include "src/traced/probes/ftrace/event_info_constants.h"
#include "src/traced/probes/ftrace/ftrace_config.h"
namespace perfetto {
// The subset of the sched_switch event's format that is used when parsing &
// encoding into the compact format.
struct CompactSchedSwitchFormat {
uint32_t event_id;
uint16_t size;
uint16_t next_pid_offset;
FtraceFieldType next_pid_type;
uint16_t next_prio_offset;
FtraceFieldType next_prio_type;
uint16_t prev_state_offset;
FtraceFieldType prev_state_type;
uint16_t next_comm_offset;
};
// Pre-parsed format of a subset of scheduling events, for use during ftrace
// parsing if compact encoding is enabled. Holds a flag, |format_valid| to
// state whether the compile-time assumptions about the format held at runtime.
// If they didn't, we cannot use the compact encoding.
struct CompactSchedEventFormat {
// If false, the rest of the struct is considered invalid.
const bool format_valid;
const CompactSchedSwitchFormat sched_switch;
};
CompactSchedEventFormat ValidateFormatForCompactSched(
const std::vector<Event>& events);
CompactSchedEventFormat InvalidCompactSchedEventFormatForTesting();
// Compact encoding configuration used at ftrace reading & parsing time.
struct CompactSchedConfig {
CompactSchedConfig(bool _enabled) : enabled(_enabled) {}
// If true, and sched_switch event is enabled, encode it in a compact format
// instead of the normal form.
const bool enabled = false;
};
CompactSchedConfig CreateCompactSchedConfig(
const FtraceConfig& request,
const CompactSchedEventFormat& compact_format);
CompactSchedConfig EnabledCompactSchedConfigForTesting();
CompactSchedConfig DisabledCompactSchedConfigForTesting();
// Mutable state for buffering parts of scheduling events, that can later be
// written out in a compact format with |WriteAndReset|. Used by the ftrace
// reader, allocated on the stack.
class CompactSchedBundleState {
public:
// Most of the state is stack-allocated, with a compile-time
// size. We work in batches of pages (see kParsingBufferSizePages in
// ftrace_controller.cc), and assume a minimum size of a sched event as
// written by the kernel (validated at runtime). We therefore can calculate
// the maximum necessary capacity for a given parsing buffer size (as
// statically asserted in ftrace_controller.cc).
// Note: be careful not to align the individual buffers at a multiple of the
// cache size.
// TODO(rsavitski): this will need a slight rework once we add sched_waking,
// as it'll be the min size of the two events.
static constexpr size_t kMaxElements = 2560;
static constexpr size_t kMinSupportedSchedSwitchSize = 56;
static constexpr size_t kExpectedCommLength = 16;
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>*
switch_timestamp() {
return &switch_timestamp_;
}
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>*
switch_prev_state() {
return &switch_prev_state_;
}
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>*
switch_next_pid() {
return &switch_next_pid_;
}
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>*
switch_next_prio() {
return &switch_next_prio_;
}
size_t interned_switch_comms_size() const {
return interned_switch_comms_size_;
}
inline void AppendSwitchTimestamp(uint64_t timestamp) {
switch_timestamp_.Append(timestamp - last_switch_timestamp_);
last_switch_timestamp_ = timestamp;
}
// TODO(rsavitski): see if we can use the fact that comms are <16 bytes
// long when comparing them.
void InternSwitchNextComm(const char* ptr) {
// Linearly scan existing string views, ftrace reader will
// make sure this set doesn't grow too large.
base::StringView transient_view(ptr);
for (size_t i = 0; i < interned_switch_comms_size_; i++) {
if (transient_view == interned_switch_comms_[i]) {
switch_next_comm_index_.Append(i);
return;
}
}
// Unique next_comm, intern it. Null byte is not copied over.
char* start = intern_buf_ + intern_buf_write_pos_;
size_t size = transient_view.size();
memcpy(start, ptr, size);
intern_buf_write_pos_ += size;
switch_next_comm_index_.Append(interned_switch_comms_size_);
base::StringView safe_view(start, size);
interned_switch_comms_[interned_switch_comms_size_++] = safe_view;
PERFETTO_DCHECK(intern_buf_write_pos_ <= sizeof(intern_buf_));
}
// Writes out the currently buffered events, and starts the next batch
// internally.
void WriteAndReset(protos::pbzero::FtraceEventBundle* bundle);
private:
// First timestamp in a bundle is absolute. The rest are all delta-encoded,
// each relative to the preceding sched_switch timestamp.
uint64_t last_switch_timestamp_ = 0;
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>
switch_timestamp_;
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>
switch_prev_state_;
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>
switch_next_pid_;
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>
switch_next_prio_;
// Storage for interned strings (without null bytes).
char intern_buf_[kMaxElements * (kExpectedCommLength - 1)];
size_t intern_buf_write_pos_ = 0;
// Views into unique interned next_comm strings. Even if every sched_switch
// carries a unique next_comm, the ftrace reader is expected to flush the
// compact buffer way before this reaches capacity. This is since the cost of
// processing each event grows with every unique interned next_comm (as the
// interning needs to search all existing internings).
std::array<base::StringView, kMaxElements> interned_switch_comms_;
uint32_t interned_switch_comms_size_ = 0;
// One entry per sched_switch event, contains the index of the interned
// next_comm string view (i.e. array index into |interned_switch_comms|).
protozero::StackAllocated<protozero::PackedVarIntBuffer, kMaxElements>
switch_next_comm_index_;
};
} // namespace perfetto
#endif // SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_