blob: 182a5d7a216e45fa3dee76999134eeab842e6be2 [file]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_PERFORMANCE_CPU_PROFILER_SAMPLER_H_
#define SRC_PERFORMANCE_CPU_PROFILER_SAMPLER_H_
#include <fidl/fuchsia.cpu.profiler/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/process.h>
#include <lib/zx/result.h>
#include <lib/zx/task.h>
#include <lib/zx/thread.h>
#include <lib/zxdump/elf-search.h>
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <functional>
#include <memory>
#include <unordered_map>
#include <vector>
#include <src/lib/unwinder/cfi_unwinder.h>
#include <src/lib/unwinder/fp_unwinder.h>
#include <src/lib/unwinder/fuchsia.h>
#include <src/lib/unwinder/unwind.h>
#include "job_watcher.h"
#include "process_watcher.h"
#include "src/lib/fxl/memory/ref_counted.h"
#include "src/lib/fxl/memory/weak_ptr.h"
#include "symbolization_context.h"
#include "targets.h"
namespace profiler {
struct Sample {
zx_koid_t pid;
zx_koid_t tid;
std::vector<uint64_t> stack;
zx::ticks timestamp;
// TODO(https://fxbug.dev/466468564): Use std::variant<Backtrace, StackMemory> instead of separate
// fields.
std::vector<uint8_t> stack_memory;
};
using SampleCallback = std::function<void(Sample)>;
class Sampler : public fxl::RefCountedThreadSafe<Sampler> {
public:
Sampler(async_dispatcher_t* dispatcher, TargetTree targets,
std::vector<fuchsia_cpu_profiler::SamplingConfig> sample_specs,
SampleCallback sample_cb = nullptr)
: dispatcher_(dispatcher),
targets_(std::move(targets)),
sample_specs_(std::move(sample_specs)),
sample_cb_(std::move(sample_cb)),
weak_factory_(this) {}
virtual zx::result<> Start(size_t buffer_size_mb);
virtual zx::result<> Stop();
// Return the information needed to symbolize the samples
zx::result<profiler::SymbolizationContext> GetContexts();
fxl::WeakPtr<Sampler> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
std::unordered_map<zx_koid_t, std::string> GetProcessNames() {
std::unordered_map<zx_koid_t, std::string> names;
(void)targets_.ForEachProcess(
[&names](std::span<const zx_koid_t>, const ProcessTarget& target) -> zx::result<> {
names[target.pid] = target.name;
return zx::ok();
});
return names;
}
std::unordered_map<zx_koid_t, std::string> GetThreadNames() {
std::unordered_map<zx_koid_t, std::string> names;
(void)targets_.ForEachProcess(
[&names](std::span<const zx_koid_t>, const ProcessTarget& target) -> zx::result<> {
for (const auto& [tid, thread] : target.threads) {
names[tid] = thread.name;
}
return zx::ok();
});
return names;
}
std::vector<zx::ticks> SamplingDurations() { return inspecting_durations_; }
virtual zx::result<> AddTarget(JobTarget&& target);
virtual ~Sampler() = default;
protected:
zx::result<> WatchTarget(const JobTarget& target);
virtual void AddThread(std::vector<zx_koid_t> job_path, zx_koid_t pid, zx_koid_t tid,
zx::thread t);
virtual void RemoveThread(std::vector<zx_koid_t> job_path, zx_koid_t pid, zx_koid_t tid);
void CollectSamples(async_dispatcher_t* dispatcher, async::TaskBase* task, zx_status_t status);
void CacheModules(const ProcessTarget& p);
async_dispatcher_t* dispatcher_;
TargetTree targets_;
std::vector<fuchsia_cpu_profiler::SamplingConfig> sample_specs_;
std::vector<zx::ticks> inspecting_durations_;
SampleCallback sample_cb_;
// Watchers cannot be moved, so we need to box them
std::unordered_map<zx_koid_t, std::unique_ptr<ProcessWatcher>> process_watchers_;
std::unordered_map<zx_koid_t, std::unique_ptr<JobWatcher>> job_watchers_;
std::map<zx_koid_t, std::map<std::vector<std::byte>, profiler::Module>> contexts_;
private:
elf_search::Searcher searcher_;
fxl::WeakPtrFactory<Sampler> weak_factory_;
async::TaskMethod<profiler::Sampler, &profiler::Sampler::CollectSamples> sample_task_{this};
};
} // namespace profiler
#endif // SRC_PERFORMANCE_CPU_PROFILER_SAMPLER_H_