blob: 1ecca33a606e9d2539da978a215c3a9b94183065 [file] [log] [blame]
// Copyright 2017 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 "garnet/bin/cobalt/app/cobalt_encoder_impl.h"
#include "garnet/bin/cobalt/app/utils.h"
namespace cobalt {
namespace encoder {
using cobalt::TimerManager;
using cobalt::TimerVal;
CobaltEncoderImpl::CobaltEncoderImpl(
std::unique_ptr<ProjectContext> project_context, ClientSecret client_secret,
ObservationStoreDispatcher* store_dispatcher,
util::EncryptedMessageMaker* encrypt_to_analyzer,
ShippingDispatcher* shipping_dispatcher, const SystemData* system_data,
TimerManager* timer_manager)
: encoder_(std::move(project_context), std::move(client_secret),
system_data),
store_dispatcher_(store_dispatcher),
encrypt_to_analyzer_(encrypt_to_analyzer),
shipping_dispatcher_(shipping_dispatcher),
timer_manager_(timer_manager) {}
template <class CB>
void CobaltEncoderImpl::AddEncodedObservation(
cobalt::encoder::Encoder::Result* result, CB callback) {
switch (result->status) {
case cobalt::encoder::Encoder::kOK:
break;
case cobalt::encoder::Encoder::kInsufficientBuildLevel:
FXL_LOG(WARNING)
<< "Cobalt metric reporting attempt with insufficient build level";
callback(Status::OK);
return;
case cobalt::encoder::Encoder::kInvalidArguments:
callback(Status::INVALID_ARGUMENTS);
return;
case cobalt::encoder::Encoder::kInvalidConfig:
case cobalt::encoder::Encoder::kEncodingFailed:
callback(Status::INTERNAL_ERROR);
FXL_LOG(WARNING) << "Cobalt internal error: " << result->status;
return;
}
auto message = std::make_unique<EncryptedMessage>();
if (!encrypt_to_analyzer_->Encrypt(*(result->observation), message.get())) {
FXL_LOG(WARNING)
<< "Cobalt internal error. Unable to encrypt observations.";
callback(Status::INTERNAL_ERROR);
}
// AddEncryptedObservation returns a StatusOr<ObservationStore::StoreStatus>.
auto result_or = store_dispatcher_->AddEncryptedObservation(
std::move(message), std::move(result->metadata));
// If the StatusOr is not ok(), that means there was no configured store for
// the metadata's backend.
if (!result_or.ok()) {
callback(Status::INTERNAL_ERROR);
}
// Unpack the inner StoreStatus and convert it to a cobalt Status.
Status status = ToCobaltStatus(result_or.ConsumeValueOrDie());
shipping_dispatcher_->NotifyObservationsAdded();
callback(status);
}
void CobaltEncoderImpl::AddStringObservation(
uint32_t metric_id, uint32_t encoding_id, fidl::StringPtr observation,
AddStringObservationCallback callback) {
auto result = encoder_.EncodeString(metric_id, encoding_id, observation);
AddEncodedObservation(&result, std::move(callback));
}
void CobaltEncoderImpl::AddIntObservation(uint32_t metric_id,
uint32_t encoding_id,
const int64_t observation,
AddIntObservationCallback callback) {
auto result = encoder_.EncodeInt(metric_id, encoding_id, observation);
AddEncodedObservation(&result, std::move(callback));
}
void CobaltEncoderImpl::AddDoubleObservation(
uint32_t metric_id, uint32_t encoding_id, const double observation,
AddDoubleObservationCallback callback) {
auto result = encoder_.EncodeDouble(metric_id, encoding_id, observation);
AddEncodedObservation(&result, std::move(callback));
}
void CobaltEncoderImpl::AddIndexObservation(
uint32_t metric_id, uint32_t encoding_id, uint32_t index,
AddIndexObservationCallback callback) {
auto result = encoder_.EncodeIndex(metric_id, encoding_id, index);
AddEncodedObservation(&result, std::move(callback));
}
void CobaltEncoderImpl::AddObservation(uint32_t metric_id, uint32_t encoding_id,
fuchsia::cobalt::Value observation,
AddObservationCallback callback) {
switch (observation.Which()) {
case fuchsia::cobalt::Value::Tag::kStringValue: {
AddStringObservation(metric_id, encoding_id, observation.string_value(),
std::move(callback));
break;
}
case fuchsia::cobalt::Value::Tag::kIntValue: {
AddIntObservation(metric_id, encoding_id, observation.int_value(),
std::move(callback));
break;
}
case fuchsia::cobalt::Value::Tag::kDoubleValue: {
AddDoubleObservation(metric_id, encoding_id, observation.double_value(),
std::move(callback));
break;
}
case fuchsia::cobalt::Value::Tag::kIndexValue: {
AddIndexObservation(metric_id, encoding_id, observation.index_value(),
std::move(callback));
break;
}
case fuchsia::cobalt::Value::Tag::kIntBucketDistribution: {
AddIntBucketDistribution(metric_id, encoding_id,
std::move(observation.int_bucket_distribution()),
std::move(callback));
break;
}
default:
callback(Status::INVALID_ARGUMENTS);
FXL_LOG(ERROR) << "Cobalt: Unrecognized value type in observation.";
return;
}
}
void CobaltEncoderImpl::AddMultipartObservation(
uint32_t metric_id,
fidl::VectorPtr<fuchsia::cobalt::ObservationValue> observation,
AddMultipartObservationCallback callback) {
cobalt::encoder::Encoder::Value value;
for (const auto& obs_val : *observation) {
switch (obs_val.value.Which()) {
case fuchsia::cobalt::Value::Tag::kStringValue: {
value.AddStringPart(obs_val.encoding_id, obs_val.name,
obs_val.value.string_value());
break;
}
case fuchsia::cobalt::Value::Tag::kIntValue: {
value.AddIntPart(obs_val.encoding_id, obs_val.name,
obs_val.value.int_value());
break;
}
case fuchsia::cobalt::Value::Tag::kDoubleValue: {
value.AddDoublePart(obs_val.encoding_id, obs_val.name,
obs_val.value.double_value());
break;
}
case fuchsia::cobalt::Value::Tag::kIndexValue: {
value.AddIndexPart(obs_val.encoding_id, obs_val.name,
obs_val.value.index_value());
break;
}
case fuchsia::cobalt::Value::Tag::kIntBucketDistribution: {
std::map<uint32_t, uint64_t> distribution_map;
for (auto it = obs_val.value.int_bucket_distribution()->begin();
obs_val.value.int_bucket_distribution()->end() != it; it++) {
distribution_map[(*it).index] = (*it).count;
}
value.AddIntBucketDistributionPart(obs_val.encoding_id, obs_val.name,
distribution_map);
break;
}
default:
callback(Status::INVALID_ARGUMENTS);
FXL_LOG(ERROR)
<< "Cobalt: Unrecognized value type for observation part "
<< obs_val.name;
return;
}
}
auto result = encoder_.Encode(metric_id, value);
AddEncodedObservation(&result, std::move(callback));
}
void CobaltEncoderImpl::AddIntBucketDistribution(
uint32_t metric_id, uint32_t encoding_id,
fidl::VectorPtr<fuchsia::cobalt::BucketDistributionEntry> distribution,
AddIntBucketDistributionCallback callback) {
std::map<uint32_t, uint64_t> distribution_map;
for (auto it = distribution->begin(); distribution->end() != it; it++) {
distribution_map[(*it).index] = (*it).count;
}
auto result = encoder_.EncodeIntBucketDistribution(metric_id, encoding_id,
distribution_map);
AddEncodedObservation(&result, std::move(callback));
}
template <class CB>
void CobaltEncoderImpl::AddTimerObservationIfReady(
std::unique_ptr<TimerVal> timer_val_ptr, CB callback) {
if (!TimerManager::isReady(timer_val_ptr)) {
// TimerManager has not received both StartTimer and EndTimer calls. Return
// OK status and wait for the other call.
callback(Status::OK);
return;
}
if (TimerManager::isMultipart(timer_val_ptr)) {
fuchsia::cobalt::ObservationValue value;
value.name = std::move(timer_val_ptr->part_name);
value.encoding_id = timer_val_ptr->encoding_id;
value.value.set_int_value(timer_val_ptr->end_timestamp -
timer_val_ptr->start_timestamp);
timer_val_ptr->observation.push_back(std::move(value));
AddMultipartObservation(timer_val_ptr->metric_id,
std::move(timer_val_ptr->observation),
std::move(callback));
} else {
AddIntObservation(
timer_val_ptr->metric_id, timer_val_ptr->encoding_id,
timer_val_ptr->end_timestamp - timer_val_ptr->start_timestamp,
std::move(callback));
}
}
void CobaltEncoderImpl::StartTimer(uint32_t metric_id, uint32_t encoding_id,
fidl::StringPtr timer_id, uint64_t timestamp,
uint32_t timeout_s,
StartTimerCallback callback) {
std::unique_ptr<TimerVal> timer_val_ptr;
auto status = timer_manager_->GetTimerValWithStart(metric_id, encoding_id,
timer_id.get(), timestamp,
timeout_s, &timer_val_ptr);
if (status != Status::OK) {
callback(status);
return;
}
AddTimerObservationIfReady(std::move(timer_val_ptr), std::move(callback));
}
void CobaltEncoderImpl::EndTimer(fidl::StringPtr timer_id, uint64_t timestamp,
uint32_t timeout_s,
EndTimerCallback callback) {
std::unique_ptr<TimerVal> timer_val_ptr;
auto status = timer_manager_->GetTimerValWithEnd(timer_id.get(), timestamp,
timeout_s, &timer_val_ptr);
if (status != Status::OK) {
callback(status);
return;
}
AddTimerObservationIfReady(std::move(timer_val_ptr), std::move(callback));
}
void CobaltEncoderImpl::EndTimerMultiPart(
fidl::StringPtr timer_id, uint64_t timestamp, fidl::StringPtr part_name,
fidl::VectorPtr<fuchsia::cobalt::ObservationValue> observation,
uint32_t timeout_s, EndTimerMultiPartCallback callback) {
std::unique_ptr<TimerVal> timer_val_ptr;
auto status = timer_manager_->GetTimerValWithEnd(
timer_id.get(), timestamp, timeout_s, part_name.get(),
std::move(observation), &timer_val_ptr);
if (status != Status::OK) {
callback(status);
return;
}
AddTimerObservationIfReady(std::move(timer_val_ptr), std::move(callback));
}
void CobaltEncoderImpl::SendObservations(SendObservationsCallback callback) {
callback(Status::OK);
}
} // namespace encoder
} // namespace cobalt