/** * * Copyright (c) 2021 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "software-diagnostics-server.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::SoftwareDiagnostics; using namespace chip::app::Clusters::SoftwareDiagnostics::Attributes; using chip::DeviceLayer::DiagnosticDataProvider; using chip::DeviceLayer::GetDiagnosticDataProvider; namespace { class SoftwareDiagosticsAttrAccess : public AttributeAccessInterface { public: // Register for the SoftwareDiagnostics cluster on all endpoints. SoftwareDiagosticsAttrAccess() : AttributeAccessInterface(Optional::Missing(), SoftwareDiagnostics::Id) {} CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; private: CHIP_ERROR ReadIfSupported(CHIP_ERROR (DiagnosticDataProvider::*getter)(uint64_t &), AttributeValueEncoder & aEncoder); CHIP_ERROR ReadThreadMetrics(AttributeValueEncoder & aEncoder); }; class SoftwareDiagnosticsCommandHandler : public CommandHandlerInterface { public: // Register for the SoftwareDiagnostics cluster on all endpoints. SoftwareDiagnosticsCommandHandler() : CommandHandlerInterface(Optional::Missing(), SoftwareDiagnostics::Id) {} void InvokeCommand(HandlerContext & handlerContext) override; CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override; }; SoftwareDiagosticsAttrAccess gAttrAccess; SoftwareDiagnosticsCommandHandler gCommandHandler; CHIP_ERROR SoftwareDiagosticsAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { if (aPath.mClusterId != SoftwareDiagnostics::Id) { // We shouldn't have been called at all. return CHIP_ERROR_INVALID_ARGUMENT; } switch (aPath.mAttributeId) { case CurrentHeapFree::Id: return ReadIfSupported(&DiagnosticDataProvider::GetCurrentHeapFree, aEncoder); case CurrentHeapUsed::Id: return ReadIfSupported(&DiagnosticDataProvider::GetCurrentHeapUsed, aEncoder); case CurrentHeapHighWatermark::Id: return ReadIfSupported(&DiagnosticDataProvider::GetCurrentHeapHighWatermark, aEncoder); case ThreadMetrics::Id: return ReadThreadMetrics(aEncoder); case Clusters::Globals::Attributes::FeatureMap::Id: { BitFlags features; if (DeviceLayer::GetDiagnosticDataProvider().SupportsWatermarks()) { features.Set(Feature::kWatermarks); } return aEncoder.Encode(features); } default: break; } return CHIP_NO_ERROR; } CHIP_ERROR SoftwareDiagosticsAttrAccess::ReadIfSupported(CHIP_ERROR (DiagnosticDataProvider::*getter)(uint64_t &), AttributeValueEncoder & aEncoder) { uint64_t data; CHIP_ERROR err = (DeviceLayer::GetDiagnosticDataProvider().*getter)(data); if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE) { data = 0; } else if (err != CHIP_NO_ERROR) { return err; } return aEncoder.Encode(data); } CHIP_ERROR SoftwareDiagosticsAttrAccess::ReadThreadMetrics(AttributeValueEncoder & aEncoder) { CHIP_ERROR err = CHIP_NO_ERROR; DeviceLayer::ThreadMetrics * threadMetrics; if (DeviceLayer::GetDiagnosticDataProvider().GetThreadMetrics(&threadMetrics) == CHIP_NO_ERROR) { err = aEncoder.EncodeList([&threadMetrics](const auto & encoder) -> CHIP_ERROR { for (DeviceLayer::ThreadMetrics * thread = threadMetrics; thread != nullptr; thread = thread->Next) { ReturnErrorOnFailure(encoder.Encode(*thread)); } return CHIP_NO_ERROR; }); DeviceLayer::GetDiagnosticDataProvider().ReleaseThreadMetrics(threadMetrics); } else { err = aEncoder.EncodeEmptyList(); } return err; } void SoftwareDiagnosticsCommandHandler::InvokeCommand(HandlerContext & handlerContext) { using Protocols::InteractionModel::Status; if (handlerContext.mRequestPath.mCommandId != Commands::ResetWatermarks::Id) { // Normal error handling return; } handlerContext.SetCommandHandled(); Status status = Status::Success; if (!DeviceLayer::GetDiagnosticDataProvider().SupportsWatermarks()) { status = Status::UnsupportedCommand; } else if (DeviceLayer::GetDiagnosticDataProvider().ResetWatermarks() != CHIP_NO_ERROR) { status = Status::Failure; } handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, status); } CHIP_ERROR SoftwareDiagnosticsCommandHandler::EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) { if (!DeviceLayer::GetDiagnosticDataProvider().SupportsWatermarks()) { // No commmands. return CHIP_NO_ERROR; } callback(Commands::ResetWatermarks::Id, context); return CHIP_NO_ERROR; } } // anonymous namespace namespace chip { namespace app { namespace Clusters { SoftwareDiagnosticsServer SoftwareDiagnosticsServer::instance; /********************************************************** * SoftwareDiagnosticsServer Implementation *********************************************************/ SoftwareDiagnosticsServer & SoftwareDiagnosticsServer::Instance() { return instance; } // Gets called when a software fault that has taken place on the Node. void SoftwareDiagnosticsServer::OnSoftwareFaultDetect(const SoftwareDiagnostics::Events::SoftwareFault::Type & softwareFault) { ChipLogDetail(Zcl, "SoftwareDiagnosticsDelegate: OnSoftwareFaultDetected"); for (auto endpoint : EnabledEndpointsWithServerCluster(SoftwareDiagnostics::Id)) { // If Software Diagnostics cluster is implemented on this endpoint EventNumber eventNumber; if (CHIP_NO_ERROR != LogEvent(softwareFault, endpoint, eventNumber)) { ChipLogError(Zcl, "SoftwareDiagnosticsDelegate: Failed to record SoftwareFault event"); } } } } // namespace Clusters } // namespace app } // namespace chip bool emberAfSoftwareDiagnosticsClusterResetWatermarksCallback(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const Commands::ResetWatermarks::DecodableType & commandData) { // Shouldn't be called at all. return false; } void MatterSoftwareDiagnosticsPluginServerInitCallback() { AttributeAccessInterfaceRegistry::Instance().Register(&gAttrAccess); CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(&gCommandHandler); }