| /* |
| * 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 <map> |
| #include <memory> |
| |
| #include <unistd.h> |
| |
| #include <unwindstack/Error.h> |
| #include <unwindstack/Regs.h> |
| |
| #include "perfetto/base/task_runner.h" |
| #include "perfetto/ext/base/optional.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. |
| base::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_ |