blob: db24b2623dcc00c0c79d01f1d95a7d3743cce0c0 [file] [log] [blame]
/**
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2016-2017 Nest Labs, Inc.
* 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.
*/
/**
* @file
* This file defines message helper functions in CHIP interaction model
*
*/
#include "MessageDefHelper.h"
#include <algorithm>
#include <app/AppConfig.h>
#include <app/InteractionModelRevision.h>
#include <app/util/basic-types.h>
#include <inttypes.h>
#include <lib/support/logging/CHIPLogging.h>
#include <stdarg.h>
#include <stdio.h>
namespace chip {
namespace app {
#if CHIP_CONFIG_IM_PRETTY_PRINT && CHIP_DETAIL_LOGGING
// this is used to run in signle thread for IM message debug purpose
namespace {
uint32_t gPrettyPrintingDepthLevel = 0;
char gLineBuffer[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
size_t gCurLineBufferSize = 0;
} // namespace
void PrettyPrintIMBlankLine()
{
if (gCurLineBufferSize)
{
// Don't need to explicitly NULL-terminate the string because
// snprintf takes care of that.
ChipLogDetail(DataManagement, "%s", gLineBuffer);
gCurLineBufferSize = 0;
}
for (uint32_t i = 0; i < gPrettyPrintingDepthLevel; i++)
{
if (sizeof(gLineBuffer) > gCurLineBufferSize)
{
size_t sizeLeft = sizeof(gLineBuffer) - gCurLineBufferSize;
size_t ret = (size_t) (snprintf(gLineBuffer + gCurLineBufferSize, sizeLeft, "\t"));
if (ret > 0)
{
gCurLineBufferSize += std::min(ret, sizeLeft);
}
}
}
}
void PrettyPrintIM(bool aIsNewLine, const char * aFmt, ...)
{
va_list args;
va_start(args, aFmt);
if (aIsNewLine)
{
PrettyPrintIMBlankLine();
}
if (sizeof(gLineBuffer) > gCurLineBufferSize)
{
size_t sizeLeft = sizeof(gLineBuffer) - gCurLineBufferSize;
size_t ret = (size_t) (vsnprintf(gLineBuffer + gCurLineBufferSize, sizeLeft, aFmt, args));
if (ret > 0)
{
gCurLineBufferSize += std::min(ret, sizeLeft);
}
}
va_end(args);
}
void IncreaseDepth()
{
gPrettyPrintingDepthLevel++;
}
void DecreaseDepth()
{
gPrettyPrintingDepthLevel--;
}
#endif
#if CHIP_CONFIG_IM_PRETTY_PRINT
CHIP_ERROR CheckIMPayload(TLV::TLVReader & aReader, int aDepth, const char * aLabel)
{
if (aDepth == 0)
{
PRETTY_PRINT("%s = ", aLabel);
}
else
{
if (TLV::IsContextTag(aReader.GetTag()))
{
PRETTY_PRINT("0x%" PRIx32 " = ", TLV::TagNumFromTag(aReader.GetTag()));
}
else if (TLV::IsProfileTag(aReader.GetTag()))
{
PRETTY_PRINT("0x%" PRIx32 "::0x%" PRIx32 " = ", TLV::ProfileIdFromTag(aReader.GetTag()),
TLV::TagNumFromTag(aReader.GetTag()));
}
else
{
// Anonymous tag, don't print anything
}
}
switch (aReader.GetType())
{
case TLV::kTLVType_Structure:
PRETTY_PRINT("{");
break;
case TLV::kTLVType_Array:
PRETTY_PRINT_SAMELINE("[");
PRETTY_PRINT("\t\t");
break;
case TLV::kTLVType_SignedInteger: {
int64_t value_s64;
ReturnErrorOnFailure(aReader.Get(value_s64));
// TODO: Figure out how to not use PRId64 here, since it's not supported
// on all libcs.
PRETTY_PRINT_SAMELINE("%" PRId64 ", ", value_s64);
break;
}
case TLV::kTLVType_UnsignedInteger: {
uint64_t value_u64;
ReturnErrorOnFailure(aReader.Get(value_u64));
// TODO: Figure out how to not use PRIu64 here, since it's not supported
// on all libcs.
PRETTY_PRINT_SAMELINE("%" PRIu64 ", ", value_u64);
break;
}
case TLV::kTLVType_FloatingPointNumber: {
double value_fp;
ReturnErrorOnFailure(aReader.Get(value_fp));
PRETTY_PRINT_SAMELINE("%f, ", value_fp);
break;
}
case TLV::kTLVType_Boolean: {
bool value_b;
ReturnErrorOnFailure(aReader.Get(value_b));
PRETTY_PRINT_SAMELINE("%s, ", value_b ? "true" : "false");
break;
}
case TLV::kTLVType_UTF8String: {
char value_s[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
#if CHIP_DETAIL_LOGGING
uint32_t readerLen = aReader.GetLength();
#endif // CHIP_DETAIL_LOGGING
CHIP_ERROR err = aReader.GetString(value_s, sizeof(value_s));
VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_BUFFER_TOO_SMALL, err);
if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
PRETTY_PRINT_SAMELINE("... (char string too long: %" PRIu32 " chars) ...", readerLen);
}
else
{
PRETTY_PRINT_SAMELINE("\"%s\" (%" PRIu32 " chars), ", value_s, readerLen);
}
break;
}
case TLV::kTLVType_ByteString: {
uint8_t value_b[CHIP_CONFIG_LOG_MESSAGE_MAX_SIZE];
uint32_t len, readerLen;
readerLen = aReader.GetLength();
CHIP_ERROR err = aReader.GetBytes(value_b, sizeof(value_b));
VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_BUFFER_TOO_SMALL, err);
PRETTY_PRINT_SAMELINE("[");
PRETTY_PRINT("\t\t");
if (readerLen < sizeof(value_b))
{
len = readerLen;
}
else
{
len = sizeof(value_b);
}
if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
PRETTY_PRINT_SAMELINE("... (byte string too long) ...");
}
else
{
for (size_t i = 0; i < len; i++)
{
PRETTY_PRINT_SAMELINE("0x%02x, ", value_b[i]);
}
}
PRETTY_PRINT("] (%" PRIu32 " bytes)", readerLen);
break;
}
case TLV::kTLVType_Null:
PRETTY_PRINT_SAMELINE("NULL");
break;
default:
PRETTY_PRINT_SAMELINE("--");
break;
}
if (aReader.GetType() == TLV::kTLVType_Structure || aReader.GetType() == TLV::kTLVType_Array)
{
const char terminating_char = (aReader.GetType() == TLV::kTLVType_Structure) ? '}' : ']';
TLV::TLVType type;
IgnoreUnusedVariable(terminating_char);
ReturnErrorOnFailure(aReader.EnterContainer(type));
CHIP_ERROR err;
while ((err = aReader.Next()) == CHIP_NO_ERROR)
{
PRETTY_PRINT_INCDEPTH();
ReturnErrorOnFailure(CheckIMPayload(aReader, aDepth + 1, aLabel));
PRETTY_PRINT_DECDEPTH();
}
PRETTY_PRINT("%c,", terminating_char);
ReturnErrorOnFailure(aReader.ExitContainer(type));
}
return CHIP_NO_ERROR;
}
#endif // CHIP_CONFIG_IM_PRETTY_PRINT
}; // namespace app
}; // namespace chip