text: // 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_cpu.h"
  
  #include <zircon/status.h>
  
  #include "harvester.h"
  #include "src/lib/fxl/logging.h"
  
  namespace harvester {
  
  namespace {
  
  // Utility function to label and append a cpu sample to the |list|. |cpu| is the
  // index returned from the kernel. |path| is the kind of sample, e.g.
  // "interrupt_count".
  void AddCpuValue(SampleList* list, size_t cpu, const std::string path,
                   dockyard::SampleValue value) {
    std::ostringstream label;
    label << "cpu:" << cpu << ":" << path;
    list->emplace_back(label.str(), value);
  }
  
  }  // namespace
  
  void GatherCpu::Gather() {
    // TODO(smbug.com/34): Determine the array size at runtime (32 is arbitrary).
    zx_info_cpu_stats_t stats[32];
    size_t actual, avail;
    zx_status_t err = zx_object_get_info(RootResource(), ZX_INFO_CPU_STATS,
                                         &stats, sizeof(stats), &actual, &avail);
    if (err != ZX_OK) {
      FXL_LOG(ERROR) << "ZX_INFO_CPU_STATS returned " << err << "("
                     << zx_status_get_string(err) << ")";
      return;
    }
    auto now = std::chrono::high_resolution_clock::now();
    auto cpu_time = std::chrono::duration_cast<std::chrono::nanoseconds>(
                        now.time_since_epoch())
                        .count();
    SampleList list;
    for (size_t i = 0; i < actual; ++i) {
      // Note: stats[i].flags are not currently recorded.
  
      // Kernel scheduler counters.
      AddCpuValue(&list, i, "reschedules", stats[i].reschedules);
      AddCpuValue(&list, i, "context_switches", stats[i].context_switches);
      AddCpuValue(&list, i, "meaningful_irq_preempts", stats[i].irq_preempts);
      AddCpuValue(&list, i, "preempts", stats[i].preempts);
      AddCpuValue(&list, i, "yields", stats[i].yields);
  
      // CPU level interrupts and exceptions.
      uint64_t busy_time =
          cpu_time > stats[i].idle_time ? cpu_time - stats[i].idle_time : 0ull;
      AddCpuValue(&list, i, "busy_time", busy_time);
      AddCpuValue(&list, i, "idle_time", stats[i].idle_time);
      AddCpuValue(&list, i, "external_hardware_interrupts", stats[i].ints);
      AddCpuValue(&list, i, "timer_interrupts", stats[i].timer_ints);
      AddCpuValue(&list, i, "timer_callbacks", stats[i].timers);
      AddCpuValue(&list, i, "syscalls", stats[i].syscalls);
  
      // Inter-processor interrupts.
      AddCpuValue(&list, i, "reschedule_ipis", stats[i].reschedule_ipis);
      AddCpuValue(&list, i, "generic_ipis", stats[i].generic_ipis);
    }
    DockyardProxyStatus status = Dockyard().SendSampleList(list);
    if (status != DockyardProxyStatus::OK) {
      FXL_LOG(ERROR) << "SendSampleList failed (" << status << ")";
    }
  }
  
  }  // namespace harvester
  