blob: c9adbd84b9fa9918b319d3a14abbf1abca63be88 [file] [log] [blame]
// Copyright 2018 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 "src/developer/system_monitor/bin/dockyard_host/dockyard_host.h"
#include <time.h>
#include <chrono>
#include <fstream>
#include <future>
#include <thread>
#include "src/developer/system_monitor/lib/gt_log.h"
namespace {
// Generic function to extract the values (without the keys) from a map.
template <typename K, typename V>
std::vector<V> GetMapValues(std::map<K, V> input) {
std::vector<V> result;
result.reserve(input.size());
for (const auto& entry : input) {
result.push_back(entry.second);
}
return result;
}
// Test a multi-step query. First the set of named kernel objects is collected,
// then the IDs are determined, the IDs are translated into strings, and final
// results are printed to the log.
void TestFetchKoidNames(DockyardHost* dockyard_host,
const dockyard::Dockyard& dockyard) {
dockyard::DockyardPathToIdMap paths = dockyard.MatchPaths("koid:", ":name");
auto names = dockyard_host->GetSampleStringsForIds(GetMapValues(paths));
if (!names) {
return;
}
if (names->size() != paths.size()) {
GT_LOG(WARNING) << "names and paths size mismatch."
<< " names " << names->size() << ", paths " << paths.size();
return;
}
auto name = names->begin();
auto path = paths.begin();
for (; name < names->end(); ++name, ++path) {
GT_LOG(INFO) << path->second << "=" << path->first << ": '" << *name << "'";
}
}
} // namespace
DockyardHost::DockyardHost() : is_connected_(false) {
// Set up callback handlers.
dockyard_.SetDockyardPathsHandler(std::bind(&DockyardHost::OnPaths, this,
std::placeholders::_1,
std::placeholders::_2));
}
void DockyardHost::StartCollectingFrom(const std::string& device_name) {
dockyard::ConnectionRequest request;
request.SetDeviceName(device_name);
bool started = dockyard_.StartCollectingFrom(
std::move(request),
[this, device_name](const dockyard::ConnectionRequest& /*request*/,
const dockyard::ConnectionResponse& response) {
if (!response.Ok()) {
GT_LOG(FATAL) << "StartCollectingFrom failed";
return;
}
OnConnection(device_name);
});
if (!started) {
GT_LOG(FATAL) << "Call StopCollectingFromDevice before calling"
" StartCollectingFrom again";
}
}
std::optional<std::future<std::unique_ptr<AsyncQuery>>>
DockyardHost::GetSamples(const std::vector<dockyard::DockyardId>& path_ids) {
// Create a query instance.
auto emplacement = std::make_unique<AsyncQuery>();
auto iter = request_id_to_async_query_.emplace(
emplacement->request.RequestId(), std::move(emplacement));
if (!iter.second) {
GT_LOG(ERROR) << "Failed to emplace query (might be out of"
" memory).";
return {};
}
auto query = iter.first->second.get();
// Fill in the request.
query->request.start_time_ns = 0;
query->request.end_time_ns = dockyard_.LatestSampleTimeNs();
query->request.sample_count = 1;
query->request.render_style = dockyard::StreamSetsRequest::RECENT;
query->request.dockyard_ids.insert(query->request.dockyard_ids.begin(),
path_ids.begin(), path_ids.end());
// Issue the request for the data.
dockyard_.GetStreamSets(std::move(iter.first->second->request),
[this](const dockyard::StreamSetsRequest& /*request*/,
const dockyard::StreamSetsResponse& response) {
OnStreamSets(response);
});
return std::optional<std::future<std::unique_ptr<AsyncQuery>>>{
iter.first->second->promise.get_future()};
}
std::optional<dockyard::SampleValue> DockyardHost::GetSampleValue(
const std::string& path) {
std::vector<dockyard::DockyardId> dockyard_ids;
dockyard_ids.emplace_back(dockyard_.GetDockyardId(path));
auto future = GetSamples(dockyard_ids);
if (!future) {
return {};
}
return future->get()->response.highest_value;
}
std::optional<std::string> DockyardHost::GetSampleString(
const std::string& path) {
std::vector<dockyard::DockyardId> dockyard_ids;
dockyard_ids.emplace_back(dockyard_.GetDockyardId(path));
auto future = GetSamples(dockyard_ids);
if (!future) {
return {};
}
dockyard::DockyardId dockyard_id = future->get()->response.highest_value;
std::string string_as_path;
if (dockyard_.GetDockyardPath(dockyard_id, &string_as_path)) {
return string_as_path;
}
return {};
}
std::optional<std::vector<const std::string>>
DockyardHost::GetSampleStringsForIds(
const std::vector<dockyard::DockyardId>& path_ids) {
auto future = GetSamples(path_ids);
if (!future) {
return {};
}
std::unique_ptr<AsyncQuery> query = future->get();
GT_LOG(DEBUG) << "GetSampleStringsForIds query "
<< dockyard::DebugPrintQuery(dockyard_, query->request,
query->response)
.str();
std::vector<const std::string> result;
for (const auto& sample_values : query->response.data_sets) {
std::string string_as_path;
if (dockyard_.GetDockyardPath(sample_values[0], &string_as_path)) {
result.emplace_back(string_as_path);
} else {
result.emplace_back("<not found>");
}
}
return result;
}
void DockyardHost::OnConnection(const std::string& device_name) {
GT_LOG(DEBUG) << "Connection from " << device_name;
is_connected_ = true;
device_name_ = device_name;
// This might not be the right choice for all clients. For this application
// starting fresh with the new connection is a reasonable approach.
dockyard_.ResetHarvesterData();
// Run some tests.
run_tests_ = std::async([this]() {
// Give time for the dockyard to populate some samples.
std::this_thread::sleep_for(std::chrono::seconds(4));
// How much RAM does the Fuchsia device have.
GT_LOG(INFO) << "memory:device_total_bytes "
<< *GetSampleValue("memory:device_total_bytes");
// How much RAM does the Fuchsia device have.
GT_LOG(INFO) << "cpu:0:busy_time " << *GetSampleValue("cpu:0:busy_time");
// Dump the dockyard state to a file.
if (dump_state_) {
std::ofstream out_file;
out_file.open("dockyard_dump");
out_file << dockyard_.DebugDump().str();
out_file.close();
}
// Print a list of all the named kernel objects.
TestFetchKoidNames(this, dockyard_);
});
}
void DockyardHost::OnPaths(const std::vector<dockyard::PathInfo>& add,
const std::vector<dockyard::DockyardId>& remove) {
GT_LOG(DEBUG) << "OnPaths";
for (const auto& path_info : add) {
GT_LOG(DEBUG) << " add " << path_info.id << ": " << path_info.path;
path_to_id_.emplace(path_info.path, path_info.id);
id_to_path_.emplace(path_info.id, path_info.path);
}
for (const auto& dockyard_id : remove) {
GT_LOG(DEBUG) << " remove " << dockyard_id;
auto search = id_to_path_.find(dockyard_id);
if (search != id_to_path_.end()) {
path_to_id_.erase(search->second);
id_to_path_.erase(search);
}
}
}
void DockyardHost::OnStreamSets(const dockyard::StreamSetsResponse& response) {
auto search = request_id_to_async_query_.find(response.RequestId());
if (search != request_id_to_async_query_.end()) {
search->second->response = response;
search->second->promise.set_value(std::move(search->second));
request_id_to_async_query_.erase(search);
} else {
GT_LOG(INFO) << "Did not find RequestId " << response.RequestId();
}
}