| /** |
| * |
| * 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 <app-common/zap-generated/attributes/Accessors.h> |
| #include <app-common/zap-generated/cluster-objects.h> |
| #include <app-common/zap-generated/ids/Attributes.h> |
| #include <app-common/zap-generated/ids/Clusters.h> |
| #include <app/AttributeAccessInterface.h> |
| #include <app/CommandHandler.h> |
| #include <app/CommandHandlerInterface.h> |
| #include <app/ConcreteCommandPath.h> |
| #include <app/EventLogging.h> |
| #include <app/InteractionModelEngine.h> |
| #include <app/util/af.h> |
| #include <app/util/attribute-storage.h> |
| #include <lib/core/Optional.h> |
| #include <platform/DiagnosticDataProvider.h> |
| |
| 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<EndpointId>::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<EndpointId>::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<SoftwareDiagnosticsFeature> features; |
| |
| if (DeviceLayer::GetDiagnosticDataProvider().SupportsWatermarks()) |
| { |
| features.Set(SoftwareDiagnosticsFeature::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() |
| { |
| registerAttributeAccessOverride(&gAttrAccess); |
| InteractionModelEngine::GetInstance()->RegisterCommandHandler(&gCommandHandler); |
| } |