diff --git a/config/esp32/components/chip/CMakeLists.txt b/config/esp32/components/chip/CMakeLists.txt index 0047cfecc70a1c..35815c12fd87fc 100644 --- a/config/esp32/components/chip/CMakeLists.txt +++ b/config/esp32/components/chip/CMakeLists.txt @@ -298,6 +298,11 @@ if (CONFIG_ENABLE_ESP_INSIGHTS_TRACE) chip_gn_arg_append("matter_trace_config" "\"${CHIP_ROOT}/src/tracing/esp32_trace:esp32_trace_tracing\"") endif() +if (CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE) + chip_gn_arg_bool("matter_enable_tracing_support" "true") + chip_gn_arg_append("matter_trace_config" "\"${CHIP_ROOT}/src/tracing/esp32_diagnostic_trace:esp32_diagnostic_tracing\"") +endif() + if (CONFIG_ENABLE_ESP_INSIGHTS_SYSTEM_STATS) chip_gn_arg_append("matter_enable_esp_insights_system_stats" "true") endif() @@ -310,6 +315,10 @@ if (CONFIG_ENABLE_ESP_INSIGHTS_TRACE) target_include_directories(${COMPONENT_LIB} INTERFACE "${CHIP_ROOT}/src/tracing/esp32_trace/include") endif() +if (CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE) + target_include_directories(${COMPONENT_LIB} INTERFACE "${CHIP_ROOT}/src/tracing/esp32_diagnostic_trace/include") +endif() + if (CONFIG_CHIP_DEVICE_ENABLE_DYNAMIC_SERVER) chip_gn_arg_append("chip_build_controller_dynamic_server" "true") endif() @@ -371,6 +380,11 @@ if (CONFIG_ENABLE_ESP_INSIGHTS_TRACE) list(APPEND chip_libraries "${CMAKE_CURRENT_BINARY_DIR}/lib/libEsp32TracingBackend.a") endif() +if (CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE) + list(APPEND chip_libraries "${CMAKE_CURRENT_BINARY_DIR}/lib/libEsp32DiagnosticsBackend.a") +endif() + + # When using the pregenerated files, there is a edge case where an error appears for # undeclared argument chip_code_pre_generated_directory. To get around with it we are # disabling the --fail-on-unused-args flag. diff --git a/config/esp32/components/chip/Kconfig b/config/esp32/components/chip/Kconfig index 2d8127900eed69..4d84891e8d6a3a 100644 --- a/config/esp32/components/chip/Kconfig +++ b/config/esp32/components/chip/Kconfig @@ -999,6 +999,13 @@ menu "CHIP Device Layer" NVS namespace. If this option is enabled, the application can use an API to set a CD, the configured CD will be used for subsequent CD reads. + config ENABLE_ESP_DIAGNOSTICS_TRACE + bool "Enable ESP Platform Diagnostics for Matter" + default n + help + Enables the ESP Diagnostics platform to collect, store, and retrieve diagnostic data for the Matter protocol. + This feature helps monitor system health and performance by providing insights through diagnostics logs. + config ENABLE_ESP_INSIGHTS_TRACE bool "Enable Matter ESP Insights" depends on ESP_INSIGHTS_ENABLED @@ -1015,15 +1022,14 @@ menu "CHIP Device Layer" help This option enables the system statistics to be sent to the insights cloud. - config MAX_PERMIT_LIST_SIZE - int "Set permit list size for Insights traces" - range 5 30 - depends on ESP_INSIGHTS_ENABLED - default 20 - help - Maximum number of group entries that can be included in the permit list for reporting - the traces to insights. - + config MAX_PERMIT_LIST_SIZE + int "Set permit list size for Insights traces" + range 5 30 + depends on ESP_INSIGHTS_ENABLED || ENABLE_ESP_DIAGNOSTICS_TRACE + default 20 + help + Set the maximum number of group entries that can be included in the permit list for reporting + traces to Insights or diagnostics. This ensures proper management of trace reporting capacity. endmenu diff --git a/examples/temperature-measurement-app/esp32/main/Kconfig.projbuild b/examples/temperature-measurement-app/esp32/main/Kconfig.projbuild index 4ec2e859c0b1f5..ac3965d63fdd5a 100644 --- a/examples/temperature-measurement-app/esp32/main/Kconfig.projbuild +++ b/examples/temperature-measurement-app/esp32/main/Kconfig.projbuild @@ -83,3 +83,14 @@ depends on ENABLE_PW_RPC about available pin numbers for UART. endmenu + +menu "Platform Diagnostics" + + config END_USER_BUFFER_SIZE + int "Set buffer size for end user diagnostic data" + depends on ENABLE_ESP_DIAGNOSTICS_TRACE + default 4096 + help + Defines the buffer size (in bytes) for storing diagnostic data related to end user activity. + This buffer will hold logs and traces relevant to user interactions with the Matter protocol. +endmenu diff --git a/examples/temperature-measurement-app/esp32/main/diagnostic-logs-provider-delegate-impl.cpp b/examples/temperature-measurement-app/esp32/main/diagnostic-logs-provider-delegate-impl.cpp index 55336ea76030dd..299d2685c615b6 100644 --- a/examples/temperature-measurement-app/esp32/main/diagnostic-logs-provider-delegate-impl.cpp +++ b/examples/temperature-measurement-app/esp32/main/diagnostic-logs-provider-delegate-impl.cpp @@ -29,6 +29,10 @@ using namespace chip::app::Clusters::DiagnosticLogs; LogProvider LogProvider::sInstance; LogProvider::CrashLogContext LogProvider::sCrashLogContext; +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE +static uint32_t sIntentSize = CONFIG_END_USER_BUFFER_SIZE; +#endif + namespace { bool IsValidIntent(IntentEnum intent) { @@ -75,8 +79,14 @@ size_t LogProvider::GetSizeForIntent(IntentEnum intent) { switch (intent) { - case IntentEnum::kEndUserSupport: + case IntentEnum::kEndUserSupport: { +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE + return CircularDiagnosticBuffer::GetInstance().GetDataSize(); +#else return static_cast(endUserSupportLogEnd - endUserSupportLogStart); +#endif + } + break; case IntentEnum::kNetworkDiag: return static_cast(networkDiagnosticLogEnd - networkDiagnosticLogStart); case IntentEnum::kCrashLogs: @@ -108,8 +118,29 @@ CHIP_ERROR LogProvider::PrepareLogContextForIntent(LogContext * context, IntentE switch (intent) { case IntentEnum::kEndUserSupport: { +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE + CircularDiagnosticBuffer & diagnosticStorage = CircularDiagnosticBuffer::GetInstance(); + MutableByteSpan endUserSupportSpan(endUserBuffer, CONFIG_END_USER_BUFFER_SIZE); + + if (diagnosticStorage.IsEmptyBuffer()) + { + ChipLogError(DeviceLayer, "Empty Diagnostic buffer"); + return CHIP_ERROR_NOT_FOUND; + } + // Retrieve data from the diagnostic storage + CHIP_ERROR err = diagnosticStorage.Retrieve(endUserSupportSpan); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to retrieve data: %s", chip::ErrorStr(err)); + return err; + } + sIntentSize = endUserSupportSpan.size(); + // Now, assign the span to the EndUserSupport object or whatever is required + context->EndUserSupport.span = endUserSupportSpan; +#else context->EndUserSupport.span = ByteSpan(&endUserSupportLogStart[0], static_cast(endUserSupportLogEnd - endUserSupportLogStart)); +#endif } break; diff --git a/examples/temperature-measurement-app/esp32/main/include/diagnostic-logs-provider-delegate-impl.h b/examples/temperature-measurement-app/esp32/main/include/diagnostic-logs-provider-delegate-impl.h index 3431a54adc86a8..7f9eb3b5339f2a 100644 --- a/examples/temperature-measurement-app/esp32/main/include/diagnostic-logs-provider-delegate-impl.h +++ b/examples/temperature-measurement-app/esp32/main/include/diagnostic-logs-provider-delegate-impl.h @@ -19,12 +19,22 @@ #pragma once #include + +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE +#include +#endif + #include #if defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF) #include #endif // defined(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH) && defined(CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF) +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE +static uint8_t endUserBuffer[CONFIG_END_USER_BUFFER_SIZE]; +using namespace chip::Tracing::Diagnostics; +#endif + namespace chip { namespace app { namespace Clusters { diff --git a/examples/temperature-measurement-app/esp32/main/main.cpp b/examples/temperature-measurement-app/esp32/main/main.cpp index 9f7a73011a19a8..7594bdee84b328 100644 --- a/examples/temperature-measurement-app/esp32/main/main.cpp +++ b/examples/temperature-measurement-app/esp32/main/main.cpp @@ -52,6 +52,10 @@ #include #endif // CONFIG_ENABLE_ESP32_DEVICE_INFO_PROVIDER +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE +#include +#endif + namespace { #if CONFIG_ENABLE_ESP32_FACTORY_DATA_PROVIDER chip::DeviceLayer::ESP32FactoryDataProvider sFactoryDataProvider; @@ -83,6 +87,14 @@ extern "C" void app_main() chip::rpc::Init(); #endif +#if CONFIG_ENABLE_ESP_DIAGNOSTICS_TRACE + memset(endUserBuffer, 0, CONFIG_END_USER_BUFFER_SIZE); + CircularDiagnosticBuffer & diagnosticStorage = CircularDiagnosticBuffer::GetInstance(); + diagnosticStorage.Init(endUserBuffer, CONFIG_END_USER_BUFFER_SIZE); + static ESP32Diagnostics diagnosticBackend(diagnosticStorage); + Tracing::Register(diagnosticBackend); +#endif + ESP_LOGI(TAG, "Temperature sensor!"); // Initialize the ESP NVS layer. diff --git a/scripts/tools/check_includes_config.py b/scripts/tools/check_includes_config.py index 2e79c6f8f9cfa9..47406d15e683b1 100644 --- a/scripts/tools/check_includes_config.py +++ b/scripts/tools/check_includes_config.py @@ -167,6 +167,9 @@ 'src/tracing/json/json_tracing.cpp': {'string', 'sstream'}, 'src/tracing/json/json_tracing.h': {'fstream', 'unordered_map', 'string'}, + # esp32 diagnostic tracing + 'src/tracing/esp32_diagnostic_trace/Counter.h': {'map'}, + # esp32 tracing 'src/tracing/esp32_trace/esp32_tracing.h': {'unordered_map'}, diff --git a/src/tracing/esp32_diagnostic_trace/BUILD.gn b/src/tracing/esp32_diagnostic_trace/BUILD.gn new file mode 100644 index 00000000000000..5516968ea639af --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/BUILD.gn @@ -0,0 +1,46 @@ +# +#Copyright (c) 2024 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +config("tracing") { + include_dirs = [ "include" ] +} + +static_library("backend") { + output_name = "libEsp32DiagnosticsBackend" + output_dir = "${root_out_dir}/lib" + + sources = [ + "Counter.cpp", + "Counter.h", + "DiagnosticStorageManager.h", + "DiagnosticTracing.cpp", + "DiagnosticTracing.h", + "Diagnostics.h", + ] + + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/tracing", + ] +} + +source_set("esp32_diagnostic_tracing") { + public = [ "include/matter/tracing/macros_impl.h" ] + public_configs = [ ":tracing" ] + deps = [ ":backend" ] +} diff --git a/src/tracing/esp32_diagnostic_trace/Counter.cpp b/src/tracing/esp32_diagnostic_trace/Counter.cpp new file mode 100644 index 00000000000000..e9043823f6d06c --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/Counter.cpp @@ -0,0 +1,55 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 +#include + +namespace chip { +namespace Tracing { +namespace Diagnostics { + +std::map ESPDiagnosticCounter::mCounterList; + +void ESPDiagnosticCounter::CountInit(const char * label) +{ + if (mCounterList.find(label) != mCounterList.end()) + { + mCounterList[label]++; + } + else + { + mCounterList[label] = 1; + } +} + +uint32_t ESPDiagnosticCounter::GetInstanceCount(const char * label) const +{ + return mCounterList[label]; +} + +void ESPDiagnosticCounter::ReportMetrics(const char * label, DiagnosticStorageInterface & mStorageInstance) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + Counter counter(label, GetInstanceCount(label), esp_log_timestamp()); + err = mStorageInstance.Store(counter); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to store Counter diagnostic data")); +} + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/Counter.h b/src/tracing/esp32_diagnostic_trace/Counter.h new file mode 100644 index 00000000000000..ea882b3487d3b3 --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/Counter.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include "tracing/esp32_diagnostic_trace/Diagnostics.h" +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Tracing { +namespace Diagnostics { + +/** + * This class is used to monotonically increment the counters as per the label of the counter macro + * 'MATTER_TRACE_COUNTER(label)' + * As per the label of the counter macro, it adds the counter in the linked list with the name label if not + * present and returns the same instance and increments the value if the counter is already present + * in the list. + */ + +class ESPDiagnosticCounter +{ +public: + static ESPDiagnosticCounter & GetInstance(const char * label) + { + static ESPDiagnosticCounter instance; + CountInit(label); + return instance; + } + + uint32_t GetInstanceCount(const char * label) const; + + void ReportMetrics(const char * label, DiagnosticStorageInterface & mStorageInstance); + +private: + ESPDiagnosticCounter() {} + static std::map mCounterList; + static void CountInit(const char * label); +}; + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/DiagnosticStorageManager.h b/src/tracing/esp32_diagnostic_trace/DiagnosticStorageManager.h new file mode 100644 index 00000000000000..0a05b880dc308e --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/DiagnosticStorageManager.h @@ -0,0 +1,148 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once + +#include +#include +#include +using namespace chip::TLV; + +namespace chip { +namespace Tracing { +namespace Diagnostics { +class CircularDiagnosticBuffer : public TLVCircularBuffer, public DiagnosticStorageInterface +{ +public: + // Singleton instance getter + static CircularDiagnosticBuffer & GetInstance() + { + static CircularDiagnosticBuffer instance; + return instance; + } + + // Delete copy constructor and assignment operator to ensure singleton + CircularDiagnosticBuffer(const CircularDiagnosticBuffer &) = delete; + CircularDiagnosticBuffer & operator=(const CircularDiagnosticBuffer &) = delete; + + void Init(uint8_t * buffer, size_t bufferLength) { TLVCircularBuffer::Init(buffer, bufferLength); } + + CHIP_ERROR Store(DiagnosticEntry & entry) override + { + CircularTLVWriter writer; + writer.Init(*this); + + CHIP_ERROR err = entry.Encode(writer); + if (err != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Failed to write entry: %s", chip::ErrorStr(err)); + } + return err; + } + + CHIP_ERROR Retrieve(MutableByteSpan & span) override + { + printf("**********************************************Retrieval Started*****************************************\n"); + CHIP_ERROR err = CHIP_NO_ERROR; + TLVReader reader; + reader.Init(*this); + + TLVWriter writer; + writer.Init(span.data(), span.size()); + + TLVType outWriterContainer; + err = writer.StartContainer(AnonymousTag(), kTLVType_List, outWriterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to start container")); + + while (CHIP_NO_ERROR == reader.Next()) + { + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to read next TLV element: %s", ErrorStr(err))); + + if (reader.GetType() == kTLVType_Structure && reader.GetTag() == AnonymousTag()) + { + TLVType outerReaderContainer; + err = reader.EnterContainer(outerReaderContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to enter outer TLV container: %s", ErrorStr(err))); + + err = reader.Next(); + VerifyOrReturnError( + err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to read next TLV element in outer container: %s", ErrorStr(err))); + + if ((reader.GetType() == kTLVType_Structure) && + (reader.GetTag() == ContextTag(DIAGNOSTICS_TAG::METRIC) || + reader.GetTag() == ContextTag(DIAGNOSTICS_TAG::TRACE) || + reader.GetTag() == ContextTag(DIAGNOSTICS_TAG::COUNTER))) + { + if ((reader.GetLengthRead() - writer.GetLengthWritten()) < (writer.GetRemainingFreeLength())) + { + err = writer.CopyElement(reader); + if (err == CHIP_ERROR_BUFFER_TOO_SMALL) + { + ChipLogProgress(DeviceLayer, "Buffer too small to occupy current element"); + break; + } + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to copy TLV element")); + } + else + { + ChipLogProgress(DeviceLayer, "Buffer too small to occupy current TLV"); + break; + } + } + else + { + ChipLogError(DeviceLayer, "Unexpected TLV element in outer container"); + reader.ExitContainer(outerReaderContainer); + return CHIP_ERROR_WRONG_TLV_TYPE; + } + err = reader.ExitContainer(outerReaderContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to exit outer TLV container: %s", ErrorStr(err))); + } + else + { + ChipLogError(DeviceLayer, "Unexpected TLV element type or tag in outer container"); + } + } + + err = writer.EndContainer(outWriterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to close outer container")); + err = writer.Finalize(); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to finalize TLV writing")); + span.reduce_size(writer.GetLengthWritten()); + ChipLogProgress(DeviceLayer, "---------------Total written bytes successfully : %ld----------------\n", + writer.GetLengthWritten()); + ChipLogProgress(DeviceLayer, "Retrieval successful"); + return CHIP_NO_ERROR; + } + + bool IsEmptyBuffer() override { return DataLength() == 0; } + + uint32_t GetDataSize() override { return DataLength(); } + +private: + CircularDiagnosticBuffer() : TLVCircularBuffer(nullptr, 0) {} + ~CircularDiagnosticBuffer() override = default; +}; + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.cpp b/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.cpp new file mode 100644 index 00000000000000..2ebc9084a05504 --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.cpp @@ -0,0 +1,180 @@ + +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace Tracing { +namespace Diagnostics { + +#define LOG_HEAP_INFO(label, group, entry_exit) \ + do \ + { \ + ESP_DIAG_EVENT("MTR_TRC", "%s - %s - %s", entry_exit, label, group); \ + } while (0) + +constexpr size_t kPermitListMaxSize = CONFIG_MAX_PERMIT_LIST_SIZE; +using HashValue = uint32_t; + +// Implements a murmurhash with 0 seed. +uint32_t MurmurHash(const void * key) +{ + const uint32_t kMultiplier = 0x5bd1e995; + const uint32_t kShift = 24; + const unsigned char * data = (const unsigned char *) key; + uint32_t hash = 0; + + while (*data) + { + uint32_t value = *data++; + value *= kMultiplier; + value ^= value >> kShift; + value *= kMultiplier; + hash *= kMultiplier; + hash ^= value; + } + + hash ^= hash >> 13; + hash *= kMultiplier; + hash ^= hash >> 15; + + if (hash == 0) + { + ESP_LOGW("Tracing", "MurmurHash resulted in a hash value of 0"); + } + + return hash; +} + +HashValue gPermitList[kPermitListMaxSize] = { MurmurHash("PASESession"), + MurmurHash("CASESession"), + MurmurHash("NetworkCommissioning"), + MurmurHash("GeneralCommissioning"), + MurmurHash("OperationalCredentials"), + MurmurHash("CASEServer"), + MurmurHash("BLE"), + MurmurHash("BLE_Error"), + MurmurHash("Wifi"), + MurmurHash("Wifi_Error"), + MurmurHash("Fabric") }; // namespace + +bool IsPermitted(HashValue hashValue) +{ + for (HashValue permitted : gPermitList) + { + if (permitted == 0) + { + break; + } + if (hashValue == permitted) + { + return true; + } + } + return false; +} + +void ESP32Diagnostics::LogMessageReceived(MessageReceivedInfo & info) {} + +void ESP32Diagnostics::LogMessageSend(MessageSendInfo & info) {} + +void ESP32Diagnostics::LogNodeLookup(NodeLookupInfo & info) {} + +void ESP32Diagnostics::LogNodeDiscovered(NodeDiscoveredInfo & info) {} + +void ESP32Diagnostics::LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo & info) {} + +void ESP32Diagnostics::LogMetricEvent(const MetricEvent & event) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + switch (event.ValueType()) + { + case ValueType::kInt32: { + ESP_LOGI("mtr", "The value of %s is %ld ", event.key(), event.ValueInt32()); + Metric metric(event.key(), event.ValueInt32(), esp_log_timestamp()); + err = mStorageInstance.Store(metric); + } + break; + + case ValueType::kUInt32: { + ESP_LOGI("mtr", "The value of %s is %lu ", event.key(), event.ValueUInt32()); + Metric metric(event.key(), event.ValueUInt32(), esp_log_timestamp()); + err = mStorageInstance.Store(metric); + } + break; + + case ValueType::kChipErrorCode: + ESP_LOGI("mtr", "The value of %s is error with code %lu ", event.key(), event.ValueErrorCode()); + break; + + case ValueType::kUndefined: + ESP_LOGI("mtr", "The value of %s is undefined", event.key()); + break; + + default: + ESP_LOGI("mtr", "The value of %s is of an UNKNOWN TYPE", event.key()); + break; + } + + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to store Metric Diagnostic data")); +} + +void ESP32Diagnostics::TraceCounter(const char * label) +{ + ESPDiagnosticCounter::GetInstance(label).ReportMetrics(label, mStorageInstance); +} + +void ESP32Diagnostics::TraceBegin(const char * label, const char * group) +{ + StoreDiagnostics(label, group); +} + +void ESP32Diagnostics::TraceEnd(const char * label, const char * group) +{ + StoreDiagnostics(label, group); +} + +void ESP32Diagnostics::TraceInstant(const char * label, const char * group) +{ + StoreDiagnostics(label, group); +} + +void ESP32Diagnostics::StoreDiagnostics(const char * label, const char * group) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + HashValue hashValue = MurmurHash(group); + if (IsPermitted(hashValue)) + { + Trace trace(label, group, esp_log_timestamp()); + err = mStorageInstance.Store(trace); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "Failed to store Trace Diagnostic data")); + } +} + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.h b/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.h new file mode 100644 index 00000000000000..5ab60a31457447 --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/DiagnosticTracing.h @@ -0,0 +1,68 @@ +#pragma once + +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 +#include +#include +#include +#include + +#include +namespace chip { +namespace Tracing { +namespace Diagnostics { +/// A Backend that outputs data to chip logging. +/// +/// Structured data is formatted as json strings. +class ESP32Diagnostics : public ::chip::Tracing::Backend +{ +public: + ESP32Diagnostics(DiagnosticStorageInterface & storageInstance) : mStorageInstance(storageInstance) {} + + // Deleted copy constructor and assignment operator to prevent copying + ESP32Diagnostics(const ESP32Diagnostics &) = delete; + ESP32Diagnostics & operator=(const ESP32Diagnostics &) = delete; + + void TraceBegin(const char * label, const char * group) override; + + void TraceEnd(const char * label, const char * group) override; + + /// Trace a zero-sized event + void TraceInstant(const char * label, const char * group) override; + + void TraceCounter(const char * label) override; + + void LogMessageSend(MessageSendInfo &) override; + void LogMessageReceived(MessageReceivedInfo &) override; + + void LogNodeLookup(NodeLookupInfo &) override; + void LogNodeDiscovered(NodeDiscoveredInfo &) override; + void LogNodeDiscoveryFailed(NodeDiscoveryFailedInfo &) override; + void LogMetricEvent(const MetricEvent &) override; + void StoreDiagnostics(const char * label, const char * group); + +private: + using ValueType = MetricEvent::Value::Type; + DiagnosticStorageInterface & mStorageInstance; +}; + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/Diagnostics.h b/src/tracing/esp32_diagnostic_trace/Diagnostics.h new file mode 100644 index 00000000000000..a234480d312de4 --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/Diagnostics.h @@ -0,0 +1,252 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#pragma once +#include +#include +#include +using namespace chip::TLV; + +namespace chip { +namespace Tracing { + +namespace Diagnostics { + +enum class DIAGNOSTICS_TAG +{ + METRIC = 0, + TRACE = 1, + COUNTER = 2, + LABEL = 3, + GROUP = 4, + VALUE = 5, + TIMESTAMP = 6 +}; + +class DiagnosticEntry +{ +public: + virtual ~DiagnosticEntry() = default; + virtual CHIP_ERROR Encode(CircularTLVWriter & writer) = 0; +}; + +template +class Metric : public DiagnosticEntry +{ +public: + Metric(const char * label, T value, uint32_t timestamp) : label_(label), value_(value), timestamp_(timestamp) {} + + Metric() {} + + const char * GetLabel() const { return label_; } + T GetValue() const { return value_; } + uint32_t GetTimestamp() const { return timestamp_; } + + CHIP_ERROR Encode(CircularTLVWriter & writer) override + { + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType metricOuterContainer; + err = writer.StartContainer(AnonymousTag(), kTLVType_Structure, metricOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start outer metric container: %s", ErrorStr(err))); + TLVType metricContainer; + err = writer.StartContainer(ContextTag(DIAGNOSTICS_TAG::METRIC), kTLVType_Structure, metricContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start inner metric container: %s", ErrorStr(err))); + + // TIMESTAMP + err = writer.Put(ContextTag(DIAGNOSTICS_TAG::TIMESTAMP), timestamp_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write TIMESTAMP for METRIC : %s", ErrorStr(err))); + + // LABEL + err = writer.PutString(ContextTag(DIAGNOSTICS_TAG::LABEL), label_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write LABEL for METRIC : %s", ErrorStr(err))); + + // VALUE + err = writer.Put(ContextTag(DIAGNOSTICS_TAG::VALUE), value_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write VALUE for METRIC : %s", ErrorStr(err))); + + ChipLogProgress(DeviceLayer, "Metric Value written to storage successfully. label: %s\n", label_); + err = writer.EndContainer(metricContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end inner TLV container for Metric : %s", ErrorStr(err))); + err = writer.EndContainer(metricOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end outer TLV container for Metric : %s", ErrorStr(err))); + err = writer.Finalize(); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to Finalize writer : %s", ErrorStr(err))); + ReturnErrorOnFailure(writer.Finalize()); + return err; + } + +private: + const char * label_; + T value_; + uint32_t timestamp_; +}; + +class Trace : public DiagnosticEntry +{ +public: + Trace(const char * label, const char * group, uint32_t timestamp) : label_(label), group_(group), timestamp_(timestamp) {} + + Trace() {} + + const char * GetLabel() const { return label_; } + uint32_t GetTimestamp() const { return timestamp_; } + const char * GetGroup() const { return group_; } + + CHIP_ERROR Encode(CircularTLVWriter & writer) override + { + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType traceOuterContainer; + err = writer.StartContainer(AnonymousTag(), kTLVType_Structure, traceOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start outer trace container: %s", ErrorStr(err))); + TLVType traceContainer; + err = writer.StartContainer(ContextTag(DIAGNOSTICS_TAG::TRACE), kTLVType_Structure, traceContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start inner trace container: %s", ErrorStr(err))); + // TIMESTAMP + err = writer.Put(ContextTag(DIAGNOSTICS_TAG::TIMESTAMP), timestamp_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write TIMESTAMP for TRACE : %s", ErrorStr(err))); + + // GROUP + err = writer.PutString(ContextTag(DIAGNOSTICS_TAG::GROUP), group_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write GROUP for TRACE : %s", ErrorStr(err))); + + // LABEL + err = writer.PutString(ContextTag(DIAGNOSTICS_TAG::LABEL), label_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write LABEL for TRACE : %s", ErrorStr(err))); + + ChipLogProgress(DeviceLayer, "Trace Value written to storage successfully. label: %s value: %s\n", label_, group_); + err = writer.EndContainer(traceContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end inner TLV container for Trace : %s", ErrorStr(err))); + err = writer.EndContainer(traceOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end outer TLV container for Trace : %s", ErrorStr(err))); + err = writer.Finalize(); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to Finalize writer : %s", ErrorStr(err))); + ReturnErrorOnFailure(writer.Finalize()); + return err; + } + +private: + const char * label_; + const char * group_; + uint32_t timestamp_; +}; + +class Counter : public DiagnosticEntry +{ +public: + Counter(const char * label, uint32_t count, uint32_t timestamp) : label_(label), count_(count), timestamp_(timestamp) {} + + Counter() {} + + uint32_t GetCount() const { return count_; } + + uint32_t GetTimestamp() const { return timestamp_; } + + CHIP_ERROR Encode(CircularTLVWriter & writer) override + { + CHIP_ERROR err = CHIP_NO_ERROR; + TLVType counterOuterContainer; + err = writer.StartContainer(AnonymousTag(), kTLVType_Structure, counterOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start outer counter container: %s", ErrorStr(err))); + TLVType counterContainer; + err = writer.StartContainer(ContextTag(DIAGNOSTICS_TAG::COUNTER), kTLVType_Structure, counterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to Start inner counter container: %s", ErrorStr(err))); + + // TIMESTAMP + err = writer.Put(ContextTag(DIAGNOSTICS_TAG::TIMESTAMP), timestamp_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write TIMESTAMP for COUNTER : %s", ErrorStr(err))); + + // LABEL + err = writer.PutString(ContextTag(DIAGNOSTICS_TAG::LABEL), label_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write LABEL for COUNTER : %s", ErrorStr(err))); + + // COUNT + err = writer.Put(ContextTag(DIAGNOSTICS_TAG::COUNTER), count_); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to write VALUE for COUNTER : %s", ErrorStr(err))); + + ChipLogProgress(DeviceLayer, "Counter Value written to storage successfully label: %s count: %ld\n", label_, count_); + err = writer.EndContainer(counterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end inner TLV container for Counter : %s", ErrorStr(err))); + err = writer.EndContainer(counterOuterContainer); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, + ChipLogError(DeviceLayer, "Failed to end outer TLV container for Counter : %s", ErrorStr(err))); + err = writer.Finalize(); + VerifyOrReturnError(err == CHIP_NO_ERROR, err, ChipLogError(DeviceLayer, "Failed to Finalize writer : %s", ErrorStr(err))); + return err; + } + +private: + const char * label_; + uint32_t count_; + uint32_t timestamp_; +}; + +/** + * @brief Interface for storing and retrieving diagnostic data. + */ +class DiagnosticStorageInterface +{ +public: + /** + * @brief Virtual destructor for the interface. + */ + virtual ~DiagnosticStorageInterface() = default; + + /** + * @brief Stores a diagnostic entry. + * @param diagnostic Reference to a DiagnosticEntry object containing the + * diagnostic data to store. + * @return CHIP_ERROR Returns CHIP_NO_ERROR on success, or an appropriate error code on failure. + */ + virtual CHIP_ERROR Store(DiagnosticEntry & diagnostic) = 0; + + /** + * @brief Retrieves diagnostic data as a payload. + * @param payload Reference to a MutableByteSpan where the retrieved + * diagnostic data will be stored. + * @return CHIP_ERROR Returns CHIP_NO_ERROR on success, or an appropriate error code on failure. + */ + virtual CHIP_ERROR Retrieve(MutableByteSpan & payload) = 0; + + virtual bool IsEmptyBuffer() = 0; + + virtual uint32_t GetDataSize() = 0; +}; + +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip diff --git a/src/tracing/esp32_diagnostic_trace/include/matter/tracing/macros_impl.h b/src/tracing/esp32_diagnostic_trace/include/matter/tracing/macros_impl.h new file mode 100644 index 00000000000000..23231d1da38c8f --- /dev/null +++ b/src/tracing/esp32_diagnostic_trace/include/matter/tracing/macros_impl.h @@ -0,0 +1,52 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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. + */ +#pragma once + +/* Ensure we do not have double tracing macros defined */ +#if defined(MATTER_TRACE_BEGIN) +#error "Tracing macros seem to be double defined" +#endif + +#include + +// This gets forwarded to the multiplexed instance +#define MATTER_TRACE_BEGIN(label, group) ::chip::Tracing::Internal::Begin(label, group) +#define MATTER_TRACE_END(label, group) ::chip::Tracing::Internal::End(label, group) +#define MATTER_TRACE_INSTANT(label, group) ::chip::Tracing::Internal::Instant(label, group) +#define MATTER_TRACE_COUNTER(label) ::chip::Tracing::Internal::Counter(label) + +namespace chip { +namespace Tracing { +namespace Diagnostics { +class Scoped +{ +public: + inline Scoped(const char * label, const char * group) : mLabel(label), mGroup(group) { MATTER_TRACE_BEGIN(label, group); } + inline ~Scoped() {} + +private: + const char * mLabel; + const char * mGroup; +}; +} // namespace Diagnostics +} // namespace Tracing +} // namespace chip +#define _CONCAT_IMPL(a, b) a##b +#define _MACRO_CONCAT(a, b) _CONCAT_IMPL(a, b) + +#define MATTER_TRACE_SCOPE(label, group) ::chip::Tracing::Diagnostics::Scoped _MACRO_CONCAT(_trace_scope, __COUNTER__)(label, group)