blob: 83174d317ec07a0e07467a9a04abe03da3b6a03d [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_PROFILING_PERF_PERF_PRODUCER_H_
#define SRC_PROFILING_PERF_PERF_PRODUCER_H_
#include <unistd.h>
#include <map>
#include <memory>
#include <optional>
#include <unwindstack/Error.h>
#include <unwindstack/Regs.h>
#include "perfetto/base/task_runner.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/tracing/core/basic_types.h"
#include "perfetto/ext/tracing/core/producer.h"
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "perfetto/ext/tracing/core/tracing_service.h"
#include "src/profiling/common/callstack_trie.h"
#include "src/profiling/common/interning_output.h"
#include "src/profiling/common/unwind_support.h"
#include "src/profiling/perf/common_types.h"
#include "src/profiling/perf/event_config.h"
#include "src/profiling/perf/event_reader.h"
#include "src/profiling/perf/proc_descriptors.h"
#include "src/profiling/perf/unwinding.h"
#include "src/tracing/core/metatrace_writer.h"
// TODO(rsavitski): move to e.g. src/tracefs/.
#include "src/traced/probes/ftrace/ftrace_procfs.h"
namespace perfetto {
namespace profiling {
// TODO(rsavitski): describe the high-level architecture and threading. Rough
// summary in the mean time: three stages: (1) kernel buffer reader that parses
// the samples -> (2) callstack unwinder -> (3) interning and serialization of
// samples. This class handles stages (1) and (3) on the main thread. Unwinding
// is done by |Unwinder| on a dedicated thread.
class PerfProducer : public Producer,
public ProcDescriptorDelegate,
public Unwinder::Delegate {
public:
PerfProducer(ProcDescriptorGetter* proc_fd_getter,
base::TaskRunner* task_runner);
~PerfProducer() override = default;
PerfProducer(const PerfProducer&) = delete;
PerfProducer& operator=(const PerfProducer&) = delete;
PerfProducer(PerfProducer&&) = delete;
PerfProducer& operator=(PerfProducer&&) = delete;
void ConnectWithRetries(const char* socket_name);
// Producer impl:
void OnConnect() override;
void OnDisconnect() override;
void OnTracingSetup() override {}
void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
void StartDataSource(DataSourceInstanceID instance_id,
const DataSourceConfig& config) override;
void StopDataSource(DataSourceInstanceID instance_id) override;
void Flush(FlushRequestID flush_id,
const DataSourceInstanceID* data_source_ids,
size_t num_data_sources) override;
void ClearIncrementalState(const DataSourceInstanceID* data_source_ids,
size_t num_data_sources) override;
// ProcDescriptorDelegate impl:
void OnProcDescriptors(pid_t pid,
uid_t uid,
base::ScopedFile maps_fd,
base::ScopedFile mem_fd) override;
// Unwinder::Delegate impl (callbacks from unwinder):
void PostEmitSample(DataSourceInstanceID ds_id,
CompletedSample sample) override;
void PostEmitUnwinderSkippedSample(DataSourceInstanceID ds_id,
ParsedSample sample) override;
void PostFinishDataSourceStop(DataSourceInstanceID ds_id) override;
// Calls `cb` when all data sources have been registered.
void SetAllDataSourcesRegisteredCb(std::function<void()> cb) {
all_data_sources_registered_cb_ = cb;
}
// public for testing:
static bool ShouldRejectDueToFilter(
pid_t pid,
const TargetFilter& filter,
bool skip_cmdline,
base::FlatSet<std::string>* additional_cmdlines,
std::function<bool(std::string*)> read_proc_pid_cmdline);
private:
// State of the producer's connection to tracing service (traced).
enum State {
kNotStarted = 0,
kNotConnected,
kConnecting,
kConnected,
};
// Represents the data source scoped view of a process:
// * whether the process is in scope of the tracing session (if the latter
// specifies a callstack sampling target filter).
// * for userspace processes, the state of the (possibly asynchronous) lookup
// of /proc/<pid>/{maps,mem} file descriptors, which are necessary for
// callstack unwinding of samples.
// For kernel threads (or when sampling only kernelspace callstacks) the
// proc-fds are not necessary, so those processes transition directly to
// either kAccepted or kRejected.
// TODO(rsavitski): double-check and clarify pid reuse semantics. For
// userspace sampling, at most one incarnation of the pid is handled since we
// do not obtain new proc descriptors. But counter-only and kernel-only cases
// aren't as stateful and will keep emitting samples.
enum class ProcessTrackingStatus {
kInitial = 0,
kFdsResolving, // waiting on proc-fd lookup
kAccepted, // process relevant and ready for unwinding (for userspace -
// procfds received)
kFdsTimedOut, // proc-fd lookup timed out
kRejected // process not considered relevant for the data source
};
struct DataSourceState {
enum class Status { kActive, kShuttingDown };
DataSourceState(EventConfig _event_config,
uint64_t _tracing_session_id,
std::unique_ptr<TraceWriter> _trace_writer,
std::vector<EventReader> _per_cpu_readers)
: event_config(std::move(_event_config)),
tracing_session_id(_tracing_session_id),
trace_writer(std::move(_trace_writer)),
per_cpu_readers(std::move(_per_cpu_readers)) {}
Status status = Status::kActive;
const EventConfig event_config;
uint64_t tracing_session_id;
std::unique_ptr<TraceWriter> trace_writer;
// Indexed by cpu, vector never resized.
std::vector<EventReader> per_cpu_readers;
// Tracks the incremental state for interned entries.
InterningOutputTracker interning_output;
// Producer thread's view of sampled processes. This is the primary tracking
// structure, but a subset of updates are replicated to a similar structure
// in the |Unwinder|, which needs to track whether the necessary unwinding
// inputs for a given process' samples are ready.
std::map<pid_t, ProcessTrackingStatus> process_states;
// Additional state for EventConfig.TargetFilter: command lines we have
// decided to unwind, up to a total of additional_cmdline_count values.
base::FlatSet<std::string> additional_cmdlines;
};
// For |EmitSkippedSample|.
enum class SampleSkipReason {
kReadStage = 0, // discarded at read stage
kUnwindEnqueue, // discarded due to unwinder queue being full
kUnwindStage, // discarded at unwind stage
};
void ConnectService();
void Restart();
void ResetConnectionBackoff();
void IncreaseConnectionBackoff();
// Periodic read task which reads a batch of samples from all kernel ring
// buffers associated with the given data source.
void TickDataSourceRead(DataSourceInstanceID ds_id);
// Returns *false* if the reader has caught up with the writer position, true
// otherwise. Return value is only useful if the underlying perf_event has
// been paused (to identify when the buffer is empty). |max_samples| is a cap
// on the amount of samples that will be parsed, which might be more than the
// number of underlying records (as there might be non-sample records).
bool ReadAndParsePerCpuBuffer(EventReader* reader,
uint64_t max_samples,
DataSourceInstanceID ds_id,
DataSourceState* ds);
void InitiateDescriptorLookup(DataSourceInstanceID ds_id,
pid_t pid,
uint32_t timeout_ms);
// Do not call directly, use |InitiateDescriptorLookup|.
void StartDescriptorLookup(DataSourceInstanceID ds_id,
pid_t pid,
uint32_t timeout_ms);
void EvaluateDescriptorLookupTimeout(DataSourceInstanceID ds_id, pid_t pid);
void EmitSample(DataSourceInstanceID ds_id, CompletedSample sample);
void EmitRingBufferLoss(DataSourceInstanceID ds_id,
size_t cpu,
uint64_t records_lost);
void PostEmitSkippedSample(DataSourceInstanceID ds_id,
ParsedSample sample,
SampleSkipReason reason);
// Emit a packet indicating that a sample was relevant, but skipped as it was
// considered to be not unwindable (e.g. the process no longer exists).
void EmitSkippedSample(DataSourceInstanceID ds_id,
ParsedSample sample,
SampleSkipReason reason);
// Starts the shutdown of the given data source instance, starting with
// pausing the reader frontend. Once the reader reaches the point where all
// kernel buffers have been fully consumed, it will notify the |Unwinder| to
// proceed with the shutdown sequence. The unwinder in turn will call back to
// this producer once there are no more outstanding samples for the data
// source at the unwinding stage.
void InitiateReaderStop(DataSourceState* ds);
// Destroys the state belonging to this instance, and acks the stop to the
// tracing service.
void FinishDataSourceStop(DataSourceInstanceID ds_id);
// Immediately destroys the data source state, and instructs the unwinder to
// do the same. This is used for abrupt stops.
void PurgeDataSource(DataSourceInstanceID ds_id);
// Immediately stops the data source if this daemon's overall memory footprint
// is above the given threshold. This periodic task is started only for data
// sources that specify a limit.
void CheckMemoryFootprintPeriodic(DataSourceInstanceID ds_id,
uint32_t max_daemon_memory_kb);
// Chooses a random parameter for a callstack sampling option. Done at this
// level as the choice is shared by all data sources within a tracing session.
std::optional<ProcessSharding> GetOrChooseCallstackProcessShard(
uint64_t tracing_session_id,
uint32_t shard_count);
void StartMetatraceSource(DataSourceInstanceID ds_id, BufferID target_buffer);
// Task runner owned by the main thread.
base::TaskRunner* const task_runner_;
State state_ = kNotStarted;
const char* producer_socket_name_ = nullptr;
uint32_t connection_backoff_ms_ = 0;
// Valid and stable for the lifetime of this class.
ProcDescriptorGetter* const proc_fd_getter_;
// Owns shared memory, must outlive trace writing.
std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
// If multiple metatrace sources are enabled concurrently,
// only the first one becomes active.
std::map<DataSourceInstanceID, MetatraceWriter> metatrace_writers_;
// Interns callstacks across all data sources.
// TODO(rsavitski): for long profiling sessions, consider purging trie when it
// grows too large (at the moment purged only when no sources are active).
// TODO(rsavitski): interning sequences are monotonic for the lifetime of the
// daemon. Consider resetting them at safe points - possible when no sources
// are active, and tricky otherwise. In the latter case, it'll require
// emitting incremental sequence invalidation packets on all relevant
// sequences.
GlobalCallstackTrie callstack_trie_;
// State associated with perf-sampling data sources.
std::map<DataSourceInstanceID, DataSourceState> data_sources_;
// Unwinding stage, running on a dedicated thread.
UnwinderHandle unwinding_worker_;
// Used for tracepoint name -> id lookups. Initialized lazily, and in general
// best effort - can be null if tracefs isn't accessible.
std::unique_ptr<FtraceProcfs> tracefs_;
std::function<void()> all_data_sources_registered_cb_;
base::WeakPtrFactory<PerfProducer> weak_factory_; // keep last
};
} // namespace profiling
} // namespace perfetto
#endif // SRC_PROFILING_PERF_PERF_PRODUCER_H_