blob: 614ca62cd943788d319a2838c2f5103fd5886af6 [file] [log] [blame]
// Copyright 2019 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.
#include "gather_threads_and_cpu.h"
#include <lib/syslog/cpp/macros.h>
#include <string>
#include "gather_cpu.h"
#include "gather_tasks.h"
#include "sample_bundle.h"
#include "task_tree.h"
namespace harvester {
namespace {
// Utilities to create a SampleBundle with channel information.
class SampleBundleBuilder final {
public:
explicit SampleBundleBuilder(SampleBundle* samples)
: sample_bundle_(samples) {}
// Helper to add a value to the sample |int_sample_list_|.
void AddKoidValue(zx_koid_t koid, const std::string& path,
dockyard::SampleValue value);
void AddCpuValue(size_t cpu, const std::string& path,
dockyard::SampleValue value);
private:
SampleBundle* sample_bundle_;
SampleBundleBuilder() = delete;
};
// Add a value to the samples.
void SampleBundleBuilder::AddKoidValue(zx_koid_t koid, const std::string& path,
dockyard::SampleValue value) {
sample_bundle_->AddIntSample("koid", koid, path, value);
}
void SampleBundleBuilder::AddCpuValue(size_t cpu, const std::string& path,
dockyard::SampleValue value) {
sample_bundle_->AddIntSample("cpu", cpu, path, value);
}
} // namespace
void AddThreadStats(SampleBundle* samples,
const std::vector<TaskTree::Task>& threads,
OS* os) {
SampleBundleBuilder builder(samples);
zx_status_t status;
zx_info_thread_t info;
zx_info_thread_stats_t stats;
for (const TaskTree::Task& thread : threads) {
status = os->GetInfo<zx_info_thread_t>(thread.handle, thread.koid,
ZX_INFO_THREAD, "ZX_INFO_THREAD",
info);
if (status != ZX_OK) {
continue;
}
status = os->GetInfo<zx_info_thread_stats_t>(thread.handle, thread.koid,
ZX_INFO_THREAD_STATS,
"ZX_INFO_THREAD_STATS",
stats);
if (status != ZX_OK) {
continue;
}
builder.AddKoidValue(thread.koid, "thread_state", info.state);
builder.AddKoidValue(thread.koid, "cpu_total", stats.total_runtime);
}
}
void AddGlobalCpuSamples(SampleBundle* samples, zx_handle_t info_resource,
OS* os) {
SampleBundleBuilder builder(samples);
std::vector<zx_info_cpu_stats_t> stats;
zx_status_t status =
os->GetChildren<zx_info_cpu_stats_t>(info_resource,
0 /* no koid for info resource */,
ZX_INFO_CPU_STATS,
"ZX_INFO_CPU_STATS", stats);
if (status != ZX_OK) {
return;
}
auto cpu_time = os->HighResolutionNow();
for (size_t i = 0; i < stats.size(); ++i) {
const zx_info_cpu_stats_t& stat = stats[i];
// Note: stat.flags are not currently recorded.
// Kernel scheduler counters.
builder.AddCpuValue(i, "reschedules", stat.reschedules);
builder.AddCpuValue(i, "context_switches", stat.context_switches);
builder.AddCpuValue(i, "meaningful_irq_preempts", stat.irq_preempts);
builder.AddCpuValue(i, "preempts", stat.preempts);
builder.AddCpuValue(i, "yields", stat.yields);
// CPU level interrupts and exceptions.
uint64_t busy_time =
cpu_time > stat.idle_time ? cpu_time - stat.idle_time : 0ull;
builder.AddCpuValue(i, "busy_time", busy_time);
builder.AddCpuValue(i, "idle_time", stat.idle_time);
builder.AddCpuValue(i, "external_hardware_interrupts", stat.ints);
builder.AddCpuValue(i, "timer_interrupts", stat.timer_ints);
builder.AddCpuValue(i, "timer_callbacks", stat.timers);
builder.AddCpuValue(i, "syscalls", stat.syscalls);
// Inter-processor interrupts.
builder.AddCpuValue(i, "reschedule_ipis", stat.reschedule_ipis);
builder.AddCpuValue(i, "generic_ipis", stat.generic_ipis);
}
}
void GatherThreadsAndCpu::Gather() {
SampleBundle samples;
limiter_.Run([&] {
task_tree_.Gather();
AddTaskBasics(&samples, task_tree_.Jobs(), dockyard::KoidType::JOB);
AddTaskBasics(&samples, task_tree_.Processes(),
dockyard::KoidType::PROCESS);
AddTaskBasics(&samples, task_tree_.Threads(), dockyard::KoidType::THREAD);
});
AddThreadStats(&samples, task_tree_.Threads(), os_);
AddGlobalCpuSamples(&samples, InfoResource(), os_);
samples.Upload(DockyardPtr());
}
} // namespace harvester