blob: 482525d1f64c3ac2c515f322aa98849a180ba525 [file] [log] [blame]
/*
* Copyright (C) 2017 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_PROTO_TRANSLATION_TABLE_H_
#define SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_
#include <stdint.h>
#include <deque>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "perfetto/ext/base/scoped_file.h"
#include "src/traced/probes/ftrace/compact_sched.h"
#include "src/traced/probes/ftrace/event_info.h"
#include "src/traced/probes/ftrace/format_parser/format_parser.h"
#include "src/traced/probes/ftrace/printk_formats_parser.h"
namespace perfetto {
class FtraceProcfs;
namespace protos {
namespace pbzero {
class FtraceEventBundle;
} // namespace pbzero
} // namespace protos
// Used when reading the config to store the group and name info for the
// ftrace event.
class GroupAndName {
public:
GroupAndName(std::string_view group, std::string_view name)
: group_(group), name_(name) {}
bool operator==(const GroupAndName& other) const {
return std::tie(group_, name_) == std::tie(other.group(), other.name());
}
bool operator<(const GroupAndName& other) const {
return std::tie(group_, name_) < std::tie(other.group(), other.name());
}
const std::string& name() const { return name_; }
const std::string& group() const { return group_; }
std::string ToString() const { return group_ + "/" + name_; }
private:
std::string group_;
std::string name_;
};
inline void PrintTo(const GroupAndName& event, ::std::ostream* os) {
*os << "GroupAndName(" << event.group() << ", " << event.name() << ")";
}
bool InferFtraceType(const std::string& type_and_name,
size_t size,
bool is_signed,
FtraceFieldType* out);
class ProtoTranslationTable {
public:
struct FtracePageHeaderSpec {
FtraceEvent::Field timestamp{};
FtraceEvent::Field overwrite{};
FtraceEvent::Field size{};
};
static FtracePageHeaderSpec DefaultPageHeaderSpecForTesting();
// This method mutates the |events| and |common_fields| vectors to
// fill some of the fields and to delete unused events/fields
// before std:move'ing them into the ProtoTranslationTable.
static std::unique_ptr<ProtoTranslationTable> Create(
const FtraceProcfs* ftrace_procfs,
std::vector<Event> events,
std::vector<Field> common_fields);
virtual ~ProtoTranslationTable();
ProtoTranslationTable(const FtraceProcfs* ftrace_procfs,
const std::vector<Event>& events,
std::vector<Field> common_fields,
FtracePageHeaderSpec ftrace_page_header_spec,
CompactSchedEventFormat compact_sched_format,
PrintkMap printk_formats);
size_t largest_id() const { return largest_id_; }
const std::vector<Field>& common_fields() const { return common_fields_; }
const Field* common_pid() const {
// corner case: pKVM hypervisor pseudo-tracefs lacks common_pid
if (!common_pid_.has_value())
return nullptr;
return &common_pid_.value();
}
// Virtual for testing.
virtual const Event* GetEvent(const GroupAndName& group_and_name) const {
if (!group_and_name_to_event_.count(group_and_name))
return nullptr;
return group_and_name_to_event_.at(group_and_name);
}
const std::vector<const Event*>* GetEventsByGroup(
const std::string& group) const {
if (!group_to_events_.count(group))
return nullptr;
return &group_to_events_.at(group);
}
const Event* GetEventById(size_t id) const {
if (id == 0 || id > largest_id_)
return nullptr;
const Event* evt = &events_[id];
if (!evt->ftrace_event_id)
return nullptr;
return evt;
}
size_t EventToFtraceId(const GroupAndName& group_and_name) const {
if (!group_and_name_to_event_.count(group_and_name))
return 0;
return group_and_name_to_event_.at(group_and_name)->ftrace_event_id;
}
const std::deque<Event>& events() { return events_; }
const FtracePageHeaderSpec& ftrace_page_header_spec() const {
return ftrace_page_header_spec_;
}
// Returns the size in bytes of the "size" field in the ftrace header. This
// usually matches sizeof(void*) in the kernel (which can be != sizeof(void*)
// of user space on 32bit-user + 64-bit-kernel configurations).
inline uint16_t page_header_size_len() const {
// TODO(fmayer): Do kernel deepdive to double check this.
return ftrace_page_header_spec_.size.size;
}
// Retrieves the ftrace event from the proto translation
// table. If it does not exist, reads the format file and creates a
// new event with the proto id set to generic. Virtual for testing.
virtual const Event* GetOrCreateEvent(const GroupAndName&);
// Retrieves the ftrace event, that's going to be translated to a kprobe, from
// the proto translation table. If the event is already known and used for
// something other than a kprobe, returns nullptr.
virtual const Event* GetOrCreateKprobeEvent(const GroupAndName&);
// Removes the ftrace event from the proto translation table.
virtual void RemoveEvent(const GroupAndName&);
// This is for backwards compatibility. If a group is not specified in the
// config then the first event with that name will be returned.
const Event* GetEventByName(const std::string& name) const {
if (!name_to_events_.count(name))
return nullptr;
return name_to_events_.at(name)[0];
}
const CompactSchedEventFormat& compact_sched_format() const {
return compact_sched_format_;
}
base::StringView LookupTraceString(uint64_t address) const {
return printk_formats_.at(address);
}
private:
ProtoTranslationTable(const ProtoTranslationTable&) = delete;
ProtoTranslationTable& operator=(const ProtoTranslationTable&) = delete;
// Store strings so they can be read when writing the trace output.
const char* InternString(const std::string& str);
// The event must not already exist.
const Event* CreateEventWithProtoId(const GroupAndName& group_and_name,
uint32_t proto_field_id);
uint16_t CreateGenericEventField(const FtraceEvent::Field& ftrace_field,
Event& event);
const FtraceProcfs* ftrace_procfs_;
std::deque<Event> events_;
size_t largest_id_;
std::map<GroupAndName, const Event*> group_and_name_to_event_;
std::map<std::string, std::vector<const Event*>> name_to_events_;
std::map<std::string, std::vector<const Event*>> group_to_events_;
std::vector<Field> common_fields_;
std::optional<Field> common_pid_; // copy of entry in common_fields_
FtracePageHeaderSpec ftrace_page_header_spec_{};
std::set<std::string> interned_strings_;
CompactSchedEventFormat compact_sched_format_;
PrintkMap printk_formats_;
};
// Class for efficient 'is event with id x enabled?' checks.
// Mirrors the data in a FtraceConfig but in a format better suited
// to be consumed by CpuReader.
class EventFilter {
public:
EventFilter();
~EventFilter();
EventFilter(EventFilter&&) = default;
EventFilter& operator=(EventFilter&&) = default;
void AddEnabledEvent(size_t ftrace_event_id);
void DisableEvent(size_t ftrace_event_id);
bool IsEventEnabled(size_t ftrace_event_id) const;
std::set<size_t> GetEnabledEvents() const;
void EnableEventsFrom(const EventFilter&);
private:
EventFilter(const EventFilter&) = delete;
EventFilter& operator=(const EventFilter&) = delete;
std::vector<bool> enabled_ids_;
};
} // namespace perfetto
#endif // SRC_TRACED_PROBES_FTRACE_PROTO_TRANSLATION_TABLE_H_