| /* | 
 |  * 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_ |